home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 2004-09-27 | 207.5 KB | 5,138 lines
//-------------------------------------------------------------------------------------- // File: DXMUTGui.cs // // DirectX SDK Managed Direct3D GUI Sample Code // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- using System; using System.Collections; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace Microsoft.Samples.DirectX.UtilityToolkit { /// <summary> /// Predefined control types /// </summary> public enum ControlType { StaticText, Button, CheckBox, RadioButton, ComboBox, Slider, ListBox, EditBox, Scrollbar, } /// <summary> /// Possible states of a control /// </summary> public enum ControlState { Normal, Disabled, Hidden, Focus, MouseOver, Pressed, LastState // Should always be last } /// <summary> /// Blends colors /// </summary> public struct BlendColor { public ColorValue[] States; // Modulate colors for all possible control states public ColorValue Current; // Current color /// <summary>Initialize the color blending</summary> public void Initialize(ColorValue defaultColor, ColorValue disabledColor, ColorValue hiddenColor) { // Create the array States = new ColorValue[(int)ControlState.LastState]; for(int i = 0; i < States.Length; i++) { States[i] = defaultColor; } // Store the data States[(int)ControlState.Disabled] = disabledColor; States[(int)ControlState.Hidden] = hiddenColor; Current = hiddenColor; } /// <summary>Initialize the color blending</summary> public void Initialize(ColorValue defaultColor) { Initialize( defaultColor, new ColorValue(0.5f, 0.5f, 0.5f, 0.75f),new ColorValue()); } /// <summary>Blend the colors together</summary> public void Blend(ControlState state, float elapsedTime, float rate) { if ((States == null) || (States.Length == 0) ) return; // Nothing to do ColorValue destColor = States[(int)state]; Current = ColorOperator.Lerp(Current, destColor, 1.0f - (float)Math.Pow(rate, 30 * elapsedTime) ); } /// <summary>Blend the colors together</summary> public void Blend(ControlState state, float elapsedTime) { Blend(state, elapsedTime, 0.7f); } } /// <summary> /// Contains all the display information for a given control type /// </summary> public struct ElementHolder { public ControlType ControlType; public uint ElementIndex; public Element Element; } /// <summary> /// Contains all the display tweakables for a sub-control /// </summary> public class Element : ICloneable { #region Magic Numbers #endregion #region Instance Data public uint TextureIndex; // Index of the texture for this Element public uint FontIndex; // Index of the font for this Element public DrawTextFormat textFormat; // The Format argument to draw text public System.Drawing.Rectangle textureRect; // Bounding rectangle of this element on the composite texture public BlendColor TextureColor; public BlendColor FontColor; #endregion /// <summary>Set the texture</summary> public void SetTexture(uint tex, System.Drawing.Rectangle texRect, ColorValue defaultTextureColor) { // Store data TextureIndex = tex; textureRect = texRect; TextureColor.Initialize(defaultTextureColor); } /// <summary>Set the texture</summary> public void SetTexture(uint tex, System.Drawing.Rectangle texRect) { SetTexture(tex, texRect, Dialog.WhiteColorValue); } /// <summary>Set the font</summary> public void SetFont(uint font, ColorValue defaultFontColor, DrawTextFormat format) { // Store data FontIndex = font; textFormat = format; FontColor.Initialize(defaultFontColor); } /// <summary>Set the font</summary> public void SetFont(uint font){ SetFont(font, Dialog.WhiteColorValue, DrawTextFormat.Center | DrawTextFormat.VerticalCenter ); } /// <summary> /// Refresh this element /// </summary> public void Refresh() { if (TextureColor.States != null) TextureColor.Current = TextureColor.States[(int)ControlState.Hidden]; if (FontColor.States != null) FontColor.Current = FontColor.States[(int)ControlState.Hidden]; } #region ICloneable Members /// <summary>Clone an object</summary> public Element Clone() { Element e = new Element(); e.TextureIndex = this.TextureIndex; e.FontIndex = this.FontIndex; e.textFormat = this.textFormat; e.textureRect = this.textureRect; e.TextureColor = this.TextureColor; e.FontColor = this.FontColor; return e; } /// <summary>Clone an object</summary> object ICloneable.Clone() { throw new NotSupportedException("Use the strongly typed clone.");} #endregion } #region Dialog Resource Manager /// <summary> /// Structure for shared textures /// </summary> public class TextureNode { public string Filename; public Texture Texture; public uint Width; public uint Height; } /// <summary> /// Structure for shared fonts /// </summary> public class FontNode { public string FaceName; public Font Font; public uint Height; public FontWeight Weight; } /// <summary> /// Manages shared resources of dialogs /// </summary> public sealed class DialogResourceManager { private StateBlock dialogStateBlock; // Stateblock shared amongst all dialogs private Sprite dialogSprite; // Sprite used for drawing public StateBlock StateBlock { get { return dialogStateBlock; } } public Sprite Sprite { get { return dialogSprite; } } private Device device; // Device // Lists of textures/fonts private ArrayList textureCache = new ArrayList(); private ArrayList fontCache = new ArrayList(); #region Creation /// <summary>Do not allow creation</summary> private DialogResourceManager() { device = null; dialogSprite = null; dialogStateBlock = null; } private static DialogResourceManager localObject = null; public static DialogResourceManager GetGlobalInstance() { if (localObject == null) localObject = new DialogResourceManager(); return localObject; } #endregion /// <summary>Gets a font node from the cache</summary> public FontNode GetFontNode(int index) { return fontCache[index] as FontNode; } /// <summary>Gets a texture node from the cache</summary> public TextureNode GetTextureNode(int index) { return textureCache[index] as TextureNode; } /// <summary>Gets the device</summary> public Device Device { get { return device; } } /// <summary> /// Adds a font to the resource manager /// </summary> public int AddFont(string faceName, uint height, FontWeight weight) { // See if this font exists for(int i = 0; i < fontCache.Count; i++) { FontNode fn = fontCache[i] as FontNode; if ( (string.Compare(fn.FaceName, faceName, true) == 0) && fn.Height == height && fn.Weight == weight) { // Found it return i; } } // Doesn't exist, add a new one and try to create it FontNode newNode = new FontNode(); newNode.FaceName = faceName; newNode.Height = height; newNode.Weight = weight; fontCache.Add(newNode); int fontIndex = fontCache.Count-1; // If a device is available, try to create immediately if (device != null) CreateFont(fontIndex); return fontIndex; } /// <summary> /// Adds a texture to the resource manager /// </summary> public int AddTexture(string filename) { // See if this font exists for(int i = 0; i < textureCache.Count; i++) { TextureNode tn = textureCache[i] as TextureNode; if (string.Compare(tn.Filename, filename, true) == 0) { // Found it return i; } } // Doesn't exist, add a new one and try to create it TextureNode newNode = new TextureNode(); newNode.Filename = filename; textureCache.Add(newNode); int texIndex = textureCache.Count-1; // If a device is available, try to create immediately if (device != null) CreateTexture(texIndex); return texIndex; } /// <summary> /// Creates a font /// </summary> public void CreateFont(int font) { // Get the font node here FontNode fn = GetFontNode(font); if (fn.Font != null) fn.Font.Dispose(); // Get rid of this // Create the new font fn.Font = new Font(device, (int)fn.Height, 0, fn.Weight, 1, false, CharacterSet.Default, Precision.Default, FontQuality.Default, PitchAndFamily.DefaultPitch | PitchAndFamily.FamilyDoNotCare, fn.FaceName); } /// <summary> /// Creates a texture /// </summary> public void CreateTexture(int tex) { // Get the texture node here TextureNode tn = GetTextureNode(tex); // Make sure there's a texture to create if ((tn.Filename == null) || (tn.Filename.Length == 0)) return; // Find the texture string path = Utility.FindMediaFile(tn.Filename); // Create the new texture ImageInformation info = new ImageInformation(); tn.Texture = TextureLoader.FromFile(device, path, D3DX.Default, D3DX.Default, D3DX.Default, Usage.None, Format.Unknown, Pool.Managed, (Filter)D3DX.Default, (Filter)D3DX.Default, 0, ref info); // Store dimensions tn.Width = (uint)info.Width; tn.Height = (uint)info.Height; } #region Device event callbacks /// <summary> /// Called when the device is created /// </summary> public void OnCreateDevice(Device d) { // Store device device = d; // create fonts and textures for (int i = 0; i < fontCache.Count; i++) CreateFont(i); for (int i = 0; i < textureCache.Count; i++) CreateTexture(i); dialogSprite = new Sprite(d); // Create the sprite } /// <summary> /// Called when the device is reset /// </summary> public void OnResetDevice(Device device) { foreach(FontNode fn in fontCache) fn.Font.OnResetDevice(); if (dialogSprite != null) dialogSprite.OnResetDevice(); // Create new state block dialogStateBlock = new StateBlock(device, StateBlockType.All); } /// <summary> /// Clear any resources that need to be lost /// </summary> public void OnLostDevice() { foreach(FontNode fn in fontCache) { if ( (fn.Font != null) && (!fn.Font.Disposed) ) fn.Font.OnLostDevice(); } if (dialogSprite != null) dialogSprite.OnLostDevice(); if (dialogStateBlock != null) { dialogStateBlock.Dispose(); dialogStateBlock = null; } } /// <summary> /// Destroy any resources and clear the caches /// </summary> public void OnDestroyDevice() { foreach(FontNode fn in fontCache) fn.Font.Dispose(); foreach(TextureNode tn in textureCache) tn.Texture.Dispose(); if (dialogSprite != null) { dialogSprite.Dispose(); dialogSprite = null; } if (dialogStateBlock != null) { dialogStateBlock.Dispose(); dialogStateBlock = null; } } #endregion } #endregion /// <summary> /// All controls must be assigned to a dialog, which handles /// input and rendering for the controls. /// </summary> public class Dialog { #region Static Data public const int WheelDelta = 120; public static readonly ColorValue WhiteColorValue = new ColorValue(1.0f, 1.0f, 1.0f, 1.0f); public static readonly ColorValue TransparentWhite = new ColorValue(1.0f, 1.0f, 1.0f, 0.0f); public static readonly ColorValue BlackColorValue = new ColorValue(0.0f, 0.0f, 0.0f, 1.0f); private static Control controlFocus = null; // The control which has focus private static Control controlMouseOver = null; // The control which is hovered over private static Control controlMouseDown = null; // The control which the mouse was pressed on private static double timeRefresh = 0.0; /// <summary>Set the static refresh time</summary> public static void SetRefreshTime(float time) { timeRefresh = time; } #endregion #region Instance Data // Sample framework private Framework parent = null; public Framework SampleFramework { get { return parent; } } // Vertex information private CustomVertex.TransformedColoredTextured[] vertices; // Timing private double timeLastRefresh; // Control/Elements private ArrayList controlList = new ArrayList(); private ArrayList defaultElementList = new ArrayList(); // Captions private bool hasCaption; private string caption; private int captionHeight; private Element captionElement; private bool isDialogMinimized; // Dialog information private int dialogX, dialogY, width, height; // Colors private ColorValue topLeftColor, topRightColor, bottomLeftColor, bottomRightColor; // Fonts/Textures private ArrayList textureList = new ArrayList(); // Index into texture cache private ArrayList fontList = new ArrayList(); // Index into font cache // Dialogs private Dialog nextDialog; private Dialog prevDialog; // User Input control private bool usingNonUserEvents; private bool usingKeyboardInput; private bool usingMouseInput; #endregion #region Simple Properties/Methods /// <summary>Is the dilaog using non user events</summary> public bool IsUsingNonUserEvents { get { return usingNonUserEvents; } set { usingNonUserEvents = value; } } /// <summary>Is the dilaog using keyboard input</summary> public bool IsUsingKeyboardInput { get { return usingKeyboardInput; } set { usingKeyboardInput = value; } } /// <summary>Is the dilaog using mouse input</summary> public bool IsUsingMouseInput { get { return usingMouseInput; } set { usingMouseInput = value; } } /// <summary>Is the dilaog minimized</summary> public bool IsMinimized { get { return isDialogMinimized; } set { isDialogMinimized = value; } } /// <summary>Called to set dialog's location</summary> public void SetLocation(int x, int y) { dialogX = x; dialogY = y; UpdateVertices(); } /// <summary>The dialog's location</summary> public System.Drawing.Point Location { get {return new System.Drawing.Point(dialogX, dialogY); } set { dialogX = value.X; dialogY = value.Y; UpdateVertices(); } } /// <summary>Called to set dialog's size</summary> public void SetSize(int w, int h) { width = w; height = h;} /// <summary>Dialogs width</summary> public int Width { get { return width; } set { width = value; } } /// <summary>Dialogs height</summary> public int Height { get { return height; } set { height = value; } } /// <summary>Called to set dialog's caption</summary> public void SetCaptionText(string text) { caption = text; } /// <summary>The dialog's caption height</summary> public int CaptionHeight { get { return captionHeight; } set { captionHeight = value; } } /// <summary>Called to set dialog's caption enabled state</summary> public void SetCaptionEnabled(bool isEnabled) { hasCaption = isEnabled; } /// <summary>Called to set dialog's border colors</summary> public void SetBackgroundColors(ColorValue topLeft, ColorValue topRight, ColorValue bottomLeft, ColorValue bottomRight) { topLeftColor = topLeft; topRightColor = topRight; bottomLeftColor = bottomLeft; bottomRightColor = bottomRight; UpdateVertices(); } /// <summary>Called to set dialog's border colors</summary> public void SetBackgroundColors(ColorValue allCorners) { SetBackgroundColors(allCorners, allCorners, allCorners, allCorners); } #endregion /// <summary> /// Create a new instance of the dialog class /// </summary> public Dialog(Framework sample) { parent = sample; // store this for later use // Initialize to default state dialogX = 0; dialogY = 0; width = 0; height = 0; hasCaption = false; isDialogMinimized = false; caption = string.Empty; captionHeight = 18; topLeftColor = topRightColor = bottomLeftColor = bottomRightColor = new ColorValue(); timeLastRefresh = 0.0f; nextDialog = this; // Only one dialog prevDialog = this; // Only one dialog usingNonUserEvents = false; usingKeyboardInput = false; usingMouseInput = true; InitializeDefaultElements(); } /// <summary> /// Initialize the default elements for this dialog /// </summary> private void InitializeDefaultElements() { SetTexture(0, "UI\\DXUTControls.dds"); SetFont(0, "Arial", 14, FontWeight.Normal); //------------------------------------- // Element for the caption //------------------------------------- captionElement = new Element(); captionElement.SetFont(0, WhiteColorValue, DrawTextFormat.Left | DrawTextFormat.VerticalCenter); captionElement.SetTexture(0, new System.Drawing.Rectangle(17,269,224,18)); captionElement.TextureColor.States[(int)ControlState.Normal] = WhiteColorValue; captionElement.FontColor.States[(int)ControlState.Normal] = WhiteColorValue; // Pre-blend as we don't need to transition the state captionElement.TextureColor.Blend(ControlState.Normal, 10.0f); captionElement.FontColor.Blend(ControlState.Normal, 10.0f); Element e = new Element(); //------------------------------------- // StaticText //------------------------------------- e.SetFont(0); e.FontColor.States[(int)ControlState.Disabled] = new ColorValue(0.75f, 0.75f, 0.75f, 0.75f); // Assign the element SetDefaultElement(ControlType.StaticText, StaticText.TextElement, e); //------------------------------------- // Button - Button //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(0,0,136,54)); e.SetFont(0); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(1.0f, 1.0f, 1.0f, 0.55f); e.TextureColor.States[(int)ControlState.Pressed] = new ColorValue(1.0f, 1.0f, 1.0f, 0.85f); e.FontColor.States[(int)ControlState.MouseOver] = BlackColorValue; // Assign the element SetDefaultElement(ControlType.Button, Button.ButtonLayer, e); //------------------------------------- // Button - Fill Layer //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(136,0,136,54), TransparentWhite); e.TextureColor.States[(int)ControlState.MouseOver] = new ColorValue(1.0f, 1.0f, 1.0f, 0.6f); e.TextureColor.States[(int)ControlState.Pressed] = new ColorValue(0,0,0, 0.25f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(1.0f, 1.0f, 1.0f, 0.05f); // Assign the element SetDefaultElement(ControlType.Button, Button.FillLayer, e); //------------------------------------- // CheckBox - Box //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(0,54,27,27)); e.SetFont(0, WhiteColorValue, DrawTextFormat.Left | DrawTextFormat.VerticalCenter); e.FontColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 0.8f); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(1.0f, 1.0f, 1.0f, 0.55f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(1.0f, 1.0f, 1.0f, 0.8f); e.TextureColor.States[(int)ControlState.Pressed] = WhiteColorValue; // Assign the element SetDefaultElement(ControlType.CheckBox, Checkbox.BoxLayer, e); //------------------------------------- // CheckBox - Check //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(27,54,27,27)); // Assign the element SetDefaultElement(ControlType.CheckBox, Checkbox.CheckLayer, e); //------------------------------------- // RadioButton - Box //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(54,54,27,27)); e.SetFont(0, WhiteColorValue, DrawTextFormat.Left | DrawTextFormat.VerticalCenter); e.FontColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 0.8f); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(1.0f, 1.0f, 1.0f, 0.55f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(1.0f, 1.0f, 1.0f, 0.8f); e.TextureColor.States[(int)ControlState.Pressed] = WhiteColorValue; // Assign the element SetDefaultElement(ControlType.RadioButton, RadioButton.BoxLayer, e); //------------------------------------- // RadioButton - Check //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(81,54,27,27)); // Assign the element SetDefaultElement(ControlType.RadioButton, RadioButton.CheckLayer, e); //------------------------------------- // ComboBox - Main //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(7,81, 240, 42)); e.SetFont(0); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(0.8f, 0.8f, 0.8f, 0.55f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(0.95f, 0.95f, 0.95f, 0.6f); e.TextureColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 0.25f); e.FontColor.States[(int)ControlState.MouseOver] = new ColorValue(0,0,0,1.0f); e.FontColor.States[(int)ControlState.Pressed] = new ColorValue(0,0,0,1.0f); e.FontColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 0.8f); // Assign the element SetDefaultElement(ControlType.ComboBox, ComboBox.MainLayer, e); //------------------------------------- // ComboBox - Button //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(272,0, 53, 49)); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(1.0f, 1.0f, 1.0f, 0.55f); e.TextureColor.States[(int)ControlState.Pressed] = new ColorValue(0.55f, 0.55f, 0.55f, 1.0f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(1.0f, 1.0f, 1.0f, 0.75f); e.TextureColor.States[(int)ControlState.Disabled] = new ColorValue(1.0f, 1.0f, 1.0f, 0.25f); // Assign the element SetDefaultElement(ControlType.ComboBox, ComboBox.ComboButtonLayer, e); //------------------------------------- // ComboBox - Dropdown //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(7,123,234,142)); e.SetFont(0, BlackColorValue, DrawTextFormat.Left | DrawTextFormat.Top); // Assign the element SetDefaultElement(ControlType.ComboBox, ComboBox.DropdownLayer, e); //------------------------------------- // ComboBox - Selection //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(7,266,234,23)); e.SetFont(0, WhiteColorValue, DrawTextFormat.Left | DrawTextFormat.Top); // Assign the element SetDefaultElement(ControlType.ComboBox, ComboBox.SelectionLayer, e); //------------------------------------- // Slider - Track //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(1,290,279,41)); e.TextureColor.States[(int)ControlState.Normal] = new ColorValue(1.0f, 1.0f, 1.0f, 0.55f); e.TextureColor.States[(int)ControlState.Focus] = new ColorValue(1.0f, 1.0f, 1.0f, 0.75f); e.TextureColor.States[(int)ControlState.Disabled] = new ColorValue(1.0f, 1.0f, 1.0f, 0.25f); // Assign the element SetDefaultElement(ControlType.Slider, Slider.TrackLayer, e); //------------------------------------- // Slider - Button //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(248,55,41,41)); // Assign the element SetDefaultElement(ControlType.Slider, Slider.ButtonLayer, e); //------------------------------------- // Scrollbar - Track //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(243,144,22,11)); // Assign the element SetDefaultElement(ControlType.Scrollbar, ScrollBar.TrackLayer, e); //------------------------------------- // Scrollbar - Up Arrow //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(243,124,22,20)); e.TextureColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 1.0f); // Assign the element SetDefaultElement(ControlType.Scrollbar, ScrollBar.UpButtonLayer, e); //------------------------------------- // Scrollbar - Down Arrow //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(243,155,22,21)); e.TextureColor.States[(int)ControlState.Disabled] = new ColorValue(0.8f, 0.8f, 0.8f, 1.0f); // Assign the element SetDefaultElement(ControlType.Scrollbar, ScrollBar.DownButtonLayer, e); //------------------------------------- // Scrollbar - Button //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(266,123,20,44)); // Assign the element SetDefaultElement(ControlType.Scrollbar, ScrollBar.ThumbLayer, e); //------------------------------------- // EditBox //------------------------------------- // Element assignment: // 0 - text area // 1 - top left border // 2 - top border // 3 - top right border // 4 - left border // 5 - right border // 6 - lower left border // 7 - lower border // 8 - lower right border e.SetFont(0, BlackColorValue, DrawTextFormat.Left | DrawTextFormat.Top); // Assign the styles e.SetTexture(0, new System.Drawing.Rectangle(14,90,227,23)); SetDefaultElement(ControlType.EditBox, EditBox.TextLayer, e); e.SetTexture(0, new System.Drawing.Rectangle(8,82,6,8)); SetDefaultElement(ControlType.EditBox, EditBox.TopLeftBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(14,82,227,8)); SetDefaultElement(ControlType.EditBox, EditBox.TopBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(241,82,5,8)); SetDefaultElement(ControlType.EditBox, EditBox.TopRightBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(8,90,6,23)); SetDefaultElement(ControlType.EditBox, EditBox.LeftBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(241,90,5,23)); SetDefaultElement(ControlType.EditBox, EditBox.RightBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(8,113,6,8)); SetDefaultElement(ControlType.EditBox, EditBox.LowerLeftBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(14,113,227,8)); SetDefaultElement(ControlType.EditBox, EditBox.LowerBorder, e); e.SetTexture(0, new System.Drawing.Rectangle(241,113,5,8)); SetDefaultElement(ControlType.EditBox, EditBox.LowerRightBorder, e); //------------------------------------- // Listbox - Main //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(13,124,228,141)); e.SetFont(0, BlackColorValue, DrawTextFormat.Left | DrawTextFormat.Top); // Assign the element SetDefaultElement(ControlType.ListBox, ListBox.MainLayer, e); //------------------------------------- // Listbox - Selection //------------------------------------- e.SetTexture(0, new System.Drawing.Rectangle(17,269,224,18)); e.SetFont(0, WhiteColorValue, DrawTextFormat.Left | DrawTextFormat.Top); // Assign the element SetDefaultElement(ControlType.ListBox, ListBox.SelectionLayer, e); } /// <summary>Removes all controls from this dialog</summary> public void RemoveAllControls() { controlList.Clear(); if ( (controlFocus != null) && (controlFocus.Parent == this) ) controlFocus = null; controlMouseOver = null; } /// <summary>Clears the radio button group</summary> public void ClearRadioButtonGroup(uint groupIndex) { // Find all radio buttons with the given group number foreach(Control c in controlList) { if (c.ControlType == ControlType.RadioButton) { RadioButton rb = c as RadioButton; // Clear the radio button checked setting if (rb.ButtonGroup == groupIndex) rb.SetChecked(false, false); } } } /// <summary>Clears the combo box of all items</summary> public void ClearComboBox(int id) { ComboBox comboBox = GetComboBox(id); if (comboBox == null) return; comboBox.Clear(); } #region Message handling private static bool isDragging; /// <summary> /// Handle messages for this dialog /// </summary> public bool MessageProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { // If caption is enable, check for clicks in the caption area. if (hasCaption) { if (msg == NativeMethods.WindowMessage.LeftButtonDown || msg == NativeMethods.WindowMessage.LeftButtonDoubleClick) { // Current mouse position short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32()); short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32()); if (mouseX >= dialogX && mouseX < dialogX + width && mouseY >= dialogY && mouseY < dialogY + captionHeight) { isDragging = true; NativeMethods.SetCapture(hWnd); return true; } } else if ( (msg == NativeMethods.WindowMessage.LeftButtonUp) && isDragging) { // Current mouse position short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32()); short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32()); if (mouseX >= dialogX && mouseX < dialogX + width && mouseY >= dialogY && mouseY < dialogY + captionHeight) { NativeMethods.ReleaseCapture(); isDragging = false; return true; } } } // If the dialog is minimized, don't send any messages to controls. if (isDialogMinimized) return false; // If a control is in focus, it belongs to this dialog, and it's enabled, then give // it the first chance at handling the message. if (controlFocus != null && controlFocus.Parent == this && controlFocus.IsEnabled) { // If the control MsgProc handles it, then we don't. if (controlFocus.MsgProc(hWnd, msg, wParam, lParam)) return true; } switch(msg) { // Call OnFocusIn()/OnFocusOut() of the control that currently has the focus // as the application is activated/deactivated. This matches the Windows // behavior. case NativeMethods.WindowMessage.ActivateApplication: { if (controlFocus != null && controlFocus.Parent == this && controlFocus.IsEnabled) { if (wParam != IntPtr.Zero) controlFocus.OnFocusIn(); else controlFocus.OnFocusOut(); } } break; // Keyboard messages case NativeMethods.WindowMessage.KeyDown: case NativeMethods.WindowMessage.SystemKeyDown: case NativeMethods.WindowMessage.KeyUp: case NativeMethods.WindowMessage.SystemKeyUp: { // If a control is in focus, it belongs to this dialog, and it's enabled, then give // it the first chance at handling the message. if (controlFocus != null && controlFocus.Parent == this && controlFocus.IsEnabled) { // If the control MsgProc handles it, then we don't. if (controlFocus.HandleKeyboard(msg, wParam, lParam)) return true; } // Not yet handled, see if this matches a control's hotkey if (msg == NativeMethods.WindowMessage.KeyUp) { foreach(Control c in controlList) { // Was the hotkey hit? if (c.Hotkey == (System.Windows.Forms.Keys)wParam.ToInt32()) { // Yup! c.OnHotKey(); return true; } } } if (msg == NativeMethods.WindowMessage.KeyDown) { // If keyboard input is not enabled, this message should be ignored if (!usingKeyboardInput) return false; System.Windows.Forms.Keys key = (System.Windows.Forms.Keys)wParam.ToInt32(); switch(key) { case System.Windows.Forms.Keys.Right: case System.Windows.Forms.Keys.Down: if (controlFocus != null) { OnCycleFocus(true); return true; } break; case System.Windows.Forms.Keys.Left: case System.Windows.Forms.Keys.Up: if (controlFocus != null) { OnCycleFocus(false); return true; } break; case System.Windows.Forms.Keys.Tab: if (controlFocus == null) { FocusDefaultControl(); } else { bool shiftDown = NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ShiftKey); OnCycleFocus(!shiftDown); } return true; } } } break; // Mouse messages case NativeMethods.WindowMessage.MouseMove: case NativeMethods.WindowMessage.MouseWheel: case NativeMethods.WindowMessage.LeftButtonUp: case NativeMethods.WindowMessage.LeftButtonDown: case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.RightButtonUp: case NativeMethods.WindowMessage.RightButtonDown: case NativeMethods.WindowMessage.RightButtonDoubleClick: case NativeMethods.WindowMessage.MiddleButtonUp: case NativeMethods.WindowMessage.MiddleButtonDown: case NativeMethods.WindowMessage.MiddleButtonDoubleClick: case NativeMethods.WindowMessage.XButtonUp: case NativeMethods.WindowMessage.XButtonDown: case NativeMethods.WindowMessage.XButtonDoubleClick: { // If not accepting mouse input, return false to indicate the message should still // be handled by the application (usually to move the camera). if (!usingMouseInput) return false; // Current mouse position short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32()); short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32()); System.Drawing.Point mousePoint = new System.Drawing.Point(mouseX, mouseY); // Offset mouse point mousePoint.X -= dialogX; mousePoint.Y -= dialogY; // If caption is enabled, offset the Y coordinate by the negative of its height. if (hasCaption) mousePoint.Y -= captionHeight; // If a control is in focus, it belongs to this dialog, and it's enabled, then give // it the first chance at handling the message. if (controlFocus != null && controlFocus.Parent == this && controlFocus.IsEnabled) { // If the control MsgProc handles it, then we don't. if (controlFocus.HandleMouse(msg, mousePoint, wParam, lParam)) return true; } // Not yet handled, see if the mouse is over any controls Control control = GetControlAtPoint(mousePoint); if ((control != null) && (control.IsEnabled)) { // Let the control handle the mouse if it wants (and return true if it handles it) if (control.HandleMouse(msg, mousePoint, wParam, lParam)) return true; } else { // Mouse not over any controls in this dialog, if there was a control // which had focus it just lost it if (msg == NativeMethods.WindowMessage.LeftButtonDown && controlFocus != null && controlFocus.Parent == this) { controlFocus.OnFocusOut(); controlFocus = null; } } // Still not handled, hand this off to the dialog. Return false to indicate the // message should still be handled by the application (usually to move the camera). switch(msg) { case NativeMethods.WindowMessage.MouseMove: OnMouseMove(mousePoint); return false; } } break; } // Didn't handle this message return false; } /// <summary> /// Handle mouse moves /// </summary> private void OnMouseMove(System.Drawing.Point pt) { // If the mouse was previously hovering over a control, it's either // still over the control or has left if (controlMouseDown != null) { // If another dialog owns this control then let that dialog handle it if (controlMouseDown.Parent != this ) return; // If the same control is still under the mouse, nothing needs to be done if (controlMouseDown.ContainsPoint(pt)) return; // Mouse has moved outside the control, notify the control and continue controlMouseDown.OnMouseExit(); controlMouseDown = null; } // Figure out which control the mouse is over now Control control = GetControlAtPoint(pt); if (control != null) { controlMouseDown = control; controlMouseDown.OnMouseEnter(); } } #endregion #region Focus /// <summary> /// Request that this control has focus /// </summary> public static void RequestFocus(Control control) { if (controlFocus == control) return; // Already does if (!control.CanHaveFocus) return; // Can't have focus if (controlFocus != null) controlFocus.OnFocusOut(); // Set the control focus now control.OnFocusIn(); controlFocus = control; } /// <summary> /// Clears focus of the dialog /// </summary> public static void ClearFocus() { if (controlFocus != null) { controlFocus.OnFocusOut(); controlFocus = null; } } /// <summary> /// Cycles focus to the next available control /// </summary> private void OnCycleFocus(bool forward) { // This should only be handled by the dialog which owns the focused control, and // only if a control currently has focus if (controlFocus == null || controlFocus.Parent != this ) return; Control control = controlFocus; // Go through a bunch of controls for (int i = 0; i < 0xffff; i++) { control = (forward) ? GetNextControl(control) : GetPreviousControl(control); // If we've gone in a full circle, focus won't change if (control == controlFocus) return; // If the dialog accepts keybord input and the control can have focus then // move focus if (control.Parent.IsUsingKeyboardInput && control.CanHaveFocus) { controlFocus.OnFocusOut(); controlFocus = control; controlFocus.OnFocusIn(); return; } } throw new InvalidOperationException("Multiple dialogs are improperly chained together."); } /// <summary> /// Gets the next control /// </summary> private static Control GetNextControl(Control control) { int index = (int)control.index + 1; Dialog dialog = control.Parent; // Cycle through dialogs in the loop to find the next control. Note // that if only one control exists in all looped dialogs it will // be the returned 'next' control. while (index >= (int)dialog.controlList.Count) { dialog = dialog.nextDialog; index = 0; } return dialog.controlList[index] as Control; } /// <summary> /// Gets the previous control /// </summary> private static Control GetPreviousControl(Control control) { int index = (int)control.index - 1; Dialog dialog = control.Parent; // Cycle through dialogs in the loop to find the next control. Note // that if only one control exists in all looped dialogs it will // be the returned 'previous' control. while (index < 0) { dialog = dialog.prevDialog; if (dialog == null) dialog = control.Parent; index = dialog.controlList.Count - 1; } return dialog.controlList[index] as Control; } /// <summary> /// Sets focus to the default control of a dialog /// </summary> private void FocusDefaultControl() { // Check for a default control in this dialog foreach(Control c in controlList) { if (c.isDefault) { // Remove focus from the current control ClearFocus(); // Give focus to the default control controlFocus = c; controlFocus.OnFocusIn(); return; } } } #endregion #region Controls Methods/Properties /// <summary>Sets the control enabled property</summary> public void SetControlEnable(int id, bool isenabled) { Control c = GetControl(id); if (c == null) return; // No control to set c.IsEnabled = isenabled; } /// <summary>Gets the control enabled property</summary> public bool GetControlEnable(int id) { Control c = GetControl(id); if (c == null) return false; // No control to get return c.IsEnabled; } /// <summary>Returns the control located at a point (if one exists)</summary> public Control GetControlAtPoint(System.Drawing.Point pt) { foreach(Control c in controlList) { if (c == null) continue; if (c.IsEnabled && c.IsVisible && c.ContainsPoint(pt)) return c; } return null; } /// <summary>Returns the control located at this index(if one exists)</summary> public Control GetControl(int id) { foreach(Control c in controlList) { if (c == null) continue; if (c.ID == id) return c; } return null; } /// <summary>Returns the control located at this index of this type(if one exists)</summary> public Control GetControl(int id, ControlType typeControl) { foreach(Control c in controlList) { if (c == null) continue; if ((c.ID == id) && (c.ControlType == typeControl)) return c; } return null; } /// <summary>Returns the static text control located at this index(if one exists)</summary> public StaticText GetStaticText(int id) { return GetControl(id, ControlType.StaticText) as StaticText; } /// <summary>Returns the button control located at this index(if one exists)</summary> public Button GetButton(int id) { return GetControl(id, ControlType.Button) as Button; } /// <summary>Returns the checkbox control located at this index(if one exists)</summary> public Checkbox GetCheckbox(int id) { return GetControl(id, ControlType.CheckBox) as Checkbox; } /// <summary>Returns the radio button control located at this index(if one exists)</summary> public RadioButton GetRadioButton(int id) { return GetControl(id, ControlType.RadioButton) as RadioButton; } /// <summary>Returns the combo box control located at this index(if one exists)</summary> public ComboBox GetComboBox(int id) { return GetControl(id, ControlType.ComboBox) as ComboBox; } /// <summary>Returns the slider control located at this index(if one exists)</summary> public Slider GetSlider(int id) { return GetControl(id, ControlType.Slider) as Slider; } /// <summary>Returns the listbox control located at this index(if one exists)</summary> public ListBox GetListBox(int id) { return GetControl(id, ControlType.ListBox) as ListBox; } #endregion #region Default Elements /// <summary> /// Sets the default element /// </summary> public void SetDefaultElement(ControlType ctype, uint index, Element e) { // If this element already exists, just update it for (int i = 0; i < defaultElementList.Count; i++) { ElementHolder holder = (ElementHolder)defaultElementList[i]; if ( (holder.ControlType == ctype) && (holder.ElementIndex == index) ) { // Found it, update it holder.Element = e.Clone(); defaultElementList[i] = holder; return; } } // Couldn't find it, add a new entry ElementHolder newEntry = new ElementHolder(); newEntry.ControlType = ctype; newEntry.ElementIndex = index; newEntry.Element = e.Clone(); // Add it now defaultElementList.Add(newEntry); } /// <summary> /// Gets the default element /// </summary> public Element GetDefaultElement(ControlType ctype, uint index) { for (int i = 0; i < defaultElementList.Count; i++) { ElementHolder holder = (ElementHolder)defaultElementList[i]; if ( (holder.ControlType == ctype) && (holder.ElementIndex == index) ) { // Found it, return it return holder.Element; } } return null; } #endregion #region Texture/Font Resources /// <summary> /// Shared resource access. Indexed fonts and textures are shared among /// all the controls. /// </summary> public void SetFont(uint index, string faceName, uint height, FontWeight weight) { // Make sure the list is at least big enough to hold this index for (uint i = (uint)fontList.Count; i <= index; i++) fontList.Add((int)(-1)); int fontIndex = DialogResourceManager.GetGlobalInstance().AddFont(faceName, height, weight); fontList[(int)index] = fontIndex; } /// <summary> /// Shared resource access. Indexed fonts and textures are shared among /// all the controls. /// </summary> public FontNode GetFont(uint index) { return DialogResourceManager.GetGlobalInstance().GetFontNode((int)fontList[(int)index]); } /// <summary> /// Shared resource access. Indexed fonts and textures are shared among /// all the controls. /// </summary> public void SetTexture(uint index, string filename) { // Make sure the list is at least big enough to hold this index for (uint i = (uint)textureList.Count; i <= index; i++) textureList.Add((int)(-1)); int textureIndex = DialogResourceManager.GetGlobalInstance().AddTexture(filename); textureList[(int)index] = textureIndex; } /// <summary> /// Shared resource access. Indexed fonts and textures are shared among /// all the controls. /// </summary> public TextureNode GetTexture(uint index) { return DialogResourceManager.GetGlobalInstance().GetTextureNode((int)textureList[(int)index]); } #endregion #region Control Creation /// <summary> /// Initializes a control /// </summary> public void InitializeControl(Control control) { if (control == null) throw new ArgumentNullException("control", "You cannot pass in a null control to initialize"); // Set the index control.index = (uint)controlList.Count; // Look for a default element entires for (int i = 0; i < defaultElementList.Count; i++) { // Find any elements for this control ElementHolder holder = (ElementHolder)defaultElementList[i]; if (holder.ControlType == control.ControlType) control[holder.ElementIndex] = holder.Element; } // Initialize the control control.OnInitialize(); } /// <summary> /// Adds a control to the dialog /// </summary> public void AddControl(Control control) { // Initialize the control first InitializeControl(control); // Add this to the control list controlList.Add(control); } /// <summary>Adds a static text control to the dialog</summary> public StaticText AddStatic(int id, string text, int x, int y, int w, int h, bool isDefault) { // First create the static StaticText s = new StaticText(this); // Now call the add control method AddControl(s); // Set the properties of the static now s.ID = id; s.SetText(text); s.SetLocation(x, y); s.SetSize(w,h); s.isDefault = isDefault; return s; } /// <summary>Adds a static text control to the dialog</summary> public StaticText AddStatic(int id, string text, int x, int y, int w, int h){return AddStatic(id, text, x, y, w, h, false); } /// <summary>Adds a button control to the dialog</summary> public Button AddButton(int id, string text, int x, int y, int w, int h, System.Windows.Forms.Keys hotkey, bool isDefault) { // First create the button Button b = new Button(this); // Now call the add control method AddControl(b); // Set the properties of the button now b.ID = id; b.SetText(text); b.SetLocation(x, y); b.SetSize(w,h); b.Hotkey = hotkey; b.isDefault = isDefault; return b; } /// <summary>Adds a button control to the dialog</summary> public Button AddButton(int id, string text, int x, int y, int w, int h) { return AddButton(id, text, x, y, w, h, 0, false); } /// <summary>Adds a checkbox to the dialog</summary> public Checkbox AddCheckBox(int id, string text, int x, int y, int w, int h, bool ischecked, System.Windows.Forms.Keys hotkey, bool isDefault) { // First create the checkbox Checkbox c = new Checkbox(this); // Now call the add control method AddControl(c); // Set the properties of the button now c.ID = id; c.SetText(text); c.SetLocation(x, y); c.SetSize(w,h); c.Hotkey = hotkey; c.isDefault = isDefault; c.IsChecked = ischecked; return c; } /// <summary>Adds a checkbox control to the dialog</summary> public Checkbox AddCheckBox(int id, string text, int x, int y, int w, int h, bool ischecked) { return AddCheckBox(id, text, x, y, w, h, ischecked, 0, false); } /// <summary>Adds a radiobutton to the dialog</summary> public RadioButton AddRadioButton(int id, uint groupId, string text, int x, int y, int w, int h, bool ischecked, System.Windows.Forms.Keys hotkey, bool isDefault) { // First create the RadioButton RadioButton c = new RadioButton(this); // Now call the add control method AddControl(c); // Set the properties of the button now c.ID = id; c.ButtonGroup = groupId; c.SetText(text); c.SetLocation(x, y); c.SetSize(w,h); c.Hotkey = hotkey; c.isDefault = isDefault; c.IsChecked = ischecked; return c; } /// <summary>Adds a radio button control to the dialog</summary> public RadioButton AddRadioButton(int id, uint groupId, string text, int x, int y, int w, int h, bool ischecked) { return AddRadioButton(id, groupId, text, x, y, w, h, ischecked, 0, false); } /// <summary>Adds a combobox control to the dialog</summary> public ComboBox AddComboBox(int id, int x, int y, int w, int h, System.Windows.Forms.Keys hotkey, bool isDefault) { // First create the combo ComboBox c = new ComboBox(this); // Now call the add control method AddControl(c); // Set the properties of the button now c.ID = id; c.SetLocation(x, y); c.SetSize(w,h); c.Hotkey = hotkey; c.isDefault = isDefault; return c; } /// <summary>Adds a combobox control to the dialog</summary> public ComboBox AddComboBox(int id, int x, int y, int w, int h) { return AddComboBox(id, x, y, w, h, 0, false); } /// <summary>Adds a slider control to the dialog</summary> public Slider AddSlider(int id, int x, int y, int w, int h, int min, int max, int initialValue, bool isDefault) { // First create the slider Slider c = new Slider(this); // Now call the add control method AddControl(c); // Set the properties of the button now c.ID = id; c.SetLocation(x, y); c.SetSize(w,h); c.isDefault = isDefault; c.SetRange(min, max); c.Value = initialValue; return c; } /// <summary>Adds a slider control to the dialog</summary> public Slider AddSlider(int id, int x, int y, int w, int h) { return AddSlider(id, x, y, w, h, 0,100,50, false); } /// <summary>Adds a listbox control to the dialog</summary> public ListBox AddListBox(int id, int x, int y, int w, int h, ListBoxStyle style) { // First create the listbox ListBox c = new ListBox(this); // Now call the add control method AddControl(c); // Set the properties of the button now c.ID = id; c.SetLocation(x, y); c.SetSize(w,h); c.Style = style; return c; } /// <summary>Adds a listbox control to the dialog</summary> public ListBox AddListBox(int id, int x, int y, int w, int h) { return AddListBox(id, x, y, w, h, ListBoxStyle.SingleSelection); } /// <summary>Adds an edit box control to the dialog</summary> public EditBox AddEditBox(int id, string text, int x, int y, int w, int h, bool isDefault) { // First create the editbox EditBox c = new EditBox(this); // Now call the add control method AddControl(c); // Set the properties of the static now c.ID = id; c.Text = (text != null) ? text : string.Empty; c.SetLocation(x, y); c.SetSize(w,h); c.isDefault = isDefault; return c; } /// <summary>Adds an edit box control to the dialog</summary> public EditBox AddEditBox(int id, string text, int x, int y, int w, int h){return AddEditBox(id, text, x, y, w, h, false); } #endregion /// <summary>Render the dialog</summary> private void UpdateVertices() { vertices = new Microsoft.DirectX.Direct3D.CustomVertex.TransformedColoredTextured[] { new CustomVertex.TransformedColoredTextured(dialogX,dialogY,0.5f,1.0f,topLeftColor.ToArgb(),0.0f,0.5f), new CustomVertex.TransformedColoredTextured(dialogX + width,dialogY,0.5f,1.0f,topRightColor.ToArgb(),1.0f,0.5f), new CustomVertex.TransformedColoredTextured(dialogX+width,dialogY+height,0.5f,1.0f,bottomRightColor.ToArgb(),1.0f,1.0f), new CustomVertex.TransformedColoredTextured(dialogX,dialogY+height,0.5f,1.0f,bottomLeftColor.ToArgb(),0.0f,1.0f) }; } #region Drawing methods /// <summary>Render the dialog</summary> public void OnRender(float elapsedTime) { // See if the dialog needs to be refreshed if (timeLastRefresh < timeRefresh) { timeLastRefresh = FrameworkTimer.GetTime(); Refresh(); } Device device = DialogResourceManager.GetGlobalInstance().Device; // Set up a state block here and restore it when finished drawing all the controls DialogResourceManager.GetGlobalInstance().StateBlock.Capture(); // Set some render/texture states device.RenderState.AlphaBlendEnable = true; device.RenderState.SourceBlend = Blend.SourceAlpha; device.RenderState.DestinationBlend = Blend.InvSourceAlpha; device.RenderState.AlphaTestEnable = false; device.TextureState[0].ColorOperation = TextureOperation.SelectArg2; device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse; device.TextureState[0].AlphaOperation = TextureOperation.SelectArg1; device.TextureState[0].AlphaArgument1 = TextureArgument.Diffuse; device.RenderState.ZBufferEnable = false; // Clear vertex/pixel shader device.VertexShader = null; device.PixelShader = null; // Render if not minimized if (!isDialogMinimized) { device.VertexFormat = CustomVertex.TransformedColoredTextured.Format; device.DrawUserPrimitives(PrimitiveType.TriangleFan, 2, vertices); } // Reset states device.TextureState[0].ColorOperation = TextureOperation.Modulate; device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor; device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse; device.TextureState[0].AlphaOperation = TextureOperation.Modulate; device.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor; device.TextureState[0].AlphaArgument2 = TextureArgument.Diffuse; device.SamplerState[0].MinFilter = TextureFilter.Linear; // Set the texture up, and begin the sprite TextureNode tNode = GetTexture(0); device.SetTexture(0, tNode.Texture); DialogResourceManager.GetGlobalInstance().Sprite.Begin(SpriteFlags.DoNotSaveState); // Render the caption if it's enabled. if (hasCaption) { // DrawSprite will offset the rect down by // captionHeight, so adjust the rect higher // here to negate the effect. System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0,-captionHeight,width,0); DrawSprite(captionElement, rect); rect.Offset(5, 0); // Make a left margin string output = caption + ((isDialogMinimized) ? " (Minimized)" : null); DrawText(output, captionElement, rect, true); } // If the dialog is minimized, skip rendering // its controls. if (!isDialogMinimized) { for(int i = 0; i < controlList.Count; i++) { // Focused control is drawn last if (controlList[i] == controlFocus) continue; (controlList[i] as Control).Render(device, elapsedTime); } // Render the focus control if necessary if (controlFocus != null && controlFocus.Parent == this) controlFocus.Render(device, elapsedTime); } // End the sprite and apply the stateblock DialogResourceManager.GetGlobalInstance().Sprite.End(); DialogResourceManager.GetGlobalInstance().StateBlock.Apply(); } /// <summary> /// Refresh the dialog /// </summary> private void Refresh() { // Reset the controls if (controlFocus != null) controlFocus.OnFocusOut(); if (controlMouseOver != null) controlMouseOver.OnMouseExit(); controlFocus = null; controlMouseDown = null; controlMouseOver = null; // Refresh any controls foreach(Control c in controlList) { c.Refresh(); } if (usingKeyboardInput) FocusDefaultControl(); } /// <summary>Draw's some text</summary> public void DrawText(string text, Element element, System.Drawing.Rectangle rect, bool shadow) { // No need to draw fully transparant layers if (element.FontColor.Current.Alpha == 0) return; // Nothing to do System.Drawing.Rectangle screenRect = rect; screenRect.Offset(dialogX, dialogY); // If caption is enabled, offset the Y position by its height. if (hasCaption) screenRect.Offset(0, captionHeight); // Set the identity transform DialogResourceManager.GetGlobalInstance().Sprite.Transform = Matrix.Identity; // Get the font node here FontNode fNode = GetFont(element.FontIndex); if (shadow) { // Render the text shadowed System.Drawing.Rectangle shadowRect = screenRect; shadowRect.Offset(1, 1); fNode.Font.DrawText(DialogResourceManager.GetGlobalInstance().Sprite, text, shadowRect, element.textFormat, unchecked((int)0xff000000)); } fNode.Font.DrawText(DialogResourceManager.GetGlobalInstance().Sprite, text, screenRect, element.textFormat, element.FontColor.Current.ToArgb()); } /// <summary>Draw a sprite</summary> public void DrawSprite(Element element, System.Drawing.Rectangle rect) { // No need to draw fully transparant layers if (element.TextureColor.Current.Alpha == 0) return; // Nothing to do System.Drawing.Rectangle texRect = element.textureRect; System.Drawing.Rectangle screenRect = rect; screenRect.Offset(dialogX, dialogY); // If caption is enabled, offset the Y position by its height. if (hasCaption) screenRect.Offset(0, captionHeight); // Get the texture TextureNode tNode = GetTexture(element.TextureIndex); float scaleX = (float)screenRect.Width / (float)texRect.Width; float scaleY = (float)screenRect.Height / (float)texRect.Height; // Set the scaling transform DialogResourceManager.GetGlobalInstance().Sprite.Transform = Matrix.Scaling(scaleX, scaleY, 1.0f); // Calculate the position Vector3 pos = new Vector3(screenRect.Left, screenRect.Top, 0.0f); pos.X /= scaleX; pos.Y /= scaleY; // Finally draw the sprite DialogResourceManager.GetGlobalInstance().Sprite.Draw(tNode.Texture, texRect, new Vector3(), pos, element.TextureColor.Current.ToArgb()); } /// <summary>Draw's some text</summary> public void DrawText(string text, Element element, System.Drawing.Rectangle rect) { this.DrawText(text, element, rect, false); } /// <summary>Draw a rectangle</summary> public void DrawRectangle(System.Drawing.Rectangle rect, ColorValue color) { // Offset the rectangle rect.Offset(dialogX, dialogY); // If caption is enabled, offset the Y position by its height if (hasCaption) rect.Offset(0, captionHeight); // Get the integer value of the color int realColor = color.ToArgb(); // Create some vertices CustomVertex.TransformedColoredTextured[] verts = { new CustomVertex.TransformedColoredTextured((float)rect.Left - 0.5f, (float)rect.Top -0.5f, 0.5f, 1.0f, realColor, 0, 0), new CustomVertex.TransformedColoredTextured((float)rect.Right - 0.5f, (float)rect.Top -0.5f, 0.5f, 1.0f, realColor, 0, 0), new CustomVertex.TransformedColoredTextured((float)rect.Right - 0.5f, (float)rect.Bottom -0.5f, 0.5f, 1.0f, realColor, 0, 0), new CustomVertex.TransformedColoredTextured((float)rect.Left - 0.5f, (float)rect.Bottom -0.5f, 0.5f, 1.0f, realColor, 0, 0), }; // Get the device Device device = SampleFramework.Device; // Since we're doing our own drawing here, we need to flush the sprites DialogResourceManager.GetGlobalInstance().Sprite.Flush(); // Preserve the devices current vertex declaration using (VertexDeclaration decl = device.VertexDeclaration) { // Set the vertex format device.VertexFormat = CustomVertex.TransformedColoredTextured.Format; // Set some texture states device.TextureState[0].ColorOperation = TextureOperation.SelectArg2; device.TextureState[0].AlphaOperation = TextureOperation.SelectArg2; // Draw the rectangle device.DrawUserPrimitives(PrimitiveType.TriangleFan, 2, verts); // Reset some texture states device.TextureState[0].ColorOperation = TextureOperation.Modulate; device.TextureState[0].AlphaOperation = TextureOperation.Modulate; // Restore the vertex declaration device.VertexDeclaration = decl; } } #endregion } #region Abstract Control class /// <summary>Base class for all controls</summary> public abstract class Control { #region Instance data protected Dialog parentDialog; // Parent container public uint index; // Index within the control list public bool isDefault; // Protected members protected object localUserData; // User specificied data protected bool visible; protected bool isMouseOver; protected bool hasFocus; protected int controlId; // ID Number protected ControlType controlType; // Control type, set in constructor protected System.Windows.Forms.Keys hotKey; // Controls hotkey protected bool enabled; // Enabled/disabled flag protected System.Drawing.Rectangle boundingBox; // Rectangle defining the active region of the control protected int controlX,controlY,width,height; // Size, scale, and positioning members protected ArrayList elementList = new ArrayList(); // All display elements #endregion /// <summary>Initialize the control</summary> public virtual void OnInitialize() {} // Nothing to do here /// <summary>Render the control</summary> public virtual void Render(Device device, float elapsedTime) {} // Nothing to do here /// <summary>Message Handler</summary> public virtual bool MsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) {return false;} // Nothing to do here /// <summary>Handle the keyboard data</summary> public virtual bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) {return false;} // Nothing to do here /// <summary>Handle the mouse data</summary> public virtual bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) {return false;} // Nothing to do here /// <summary>User specified data</summary> public object UserData { get { return localUserData; } set { localUserData = value; } } /// <summary>The parent dialog of this control</summary> public Dialog Parent { get { return parentDialog; } } /// <summary>Can the control have focus</summary> public virtual bool CanHaveFocus { get { return false; } } /// <summary>Called when control gets focus</summary> public virtual void OnFocusIn() { hasFocus = true;} /// <summary>Called when control loses focus</summary> public virtual void OnFocusOut() { hasFocus = false;} /// <summary>Called when mouse goes over the control</summary> public virtual void OnMouseEnter() { isMouseOver = true;} /// <summary>Called when mouse leaves the control</summary> public virtual void OnMouseExit() { isMouseOver = false;} /// <summary>Called when the control's hotkey is hit</summary> public virtual void OnHotKey() {} // Nothing to do here /// <summary>Does the control contain this point</summary> public virtual bool ContainsPoint(System.Drawing.Point pt) { return boundingBox.Contains(pt); } /// <summary>Is the control enabled</summary> public virtual bool IsEnabled { get { return enabled; } set { enabled = value; } } /// <summary>Is the control visible</summary> public virtual bool IsVisible { get { return visible; } set { visible = value; } } /// <summary>Type of the control</summary> public virtual ControlType ControlType { get { return controlType; } } /// <summary>Unique ID of the control</summary> public virtual int ID { get { return controlId; } set { controlId = value; } } /// <summary>Called to set control's location</summary> public virtual void SetLocation(int x, int y) { controlX = x; controlY = y; UpdateRectangles(); } /// <summary>Called to set control's size</summary> public virtual void SetSize(int w, int h) { width = w; height = h; UpdateRectangles(); } /// <summary>The controls hotkey</summary> public virtual System.Windows.Forms.Keys Hotkey { get { return hotKey; } set { hotKey = value; } } /// <summary> /// Index for the elements this control has access to /// </summary> public Element this[uint index] { get { return elementList[(int)index] as Element; } set { if (value == null) throw new ArgumentNullException("ControlIndexer", "You cannot set a null element."); // Is the collection big enough? for(uint i = (uint)elementList.Count; i <= index; i++) { // Add a new one elementList.Add(new Element()); } // Update the data (with a clone) elementList[(int)index] = value.Clone(); } } /// <summary> /// Create a new instance of a control /// </summary> protected Control(Dialog parent) { controlType = ControlType.Button; parentDialog = parent; controlId = 0; index = 0; enabled = true; visible = true; isMouseOver = false; hasFocus = false; isDefault = false; controlX = 0; controlY = 0; width = 0; height = 0; } /// <summary> /// Refreshes the control /// </summary> public virtual void Refresh() { isMouseOver = false; hasFocus = false; for(int i = 0; i < elementList.Count; i++) { (elementList[i] as Element).Refresh(); } } /// <summary> /// Updates the rectangles /// </summary> protected virtual void UpdateRectangles() { boundingBox = new System.Drawing.Rectangle(controlX, controlY, width, height); } } #endregion #region StaticText control /// <summary> /// StaticText text control /// </summary> public class StaticText : Control { public const int TextElement = 0; protected string textData; // Window text /// <summary> /// Create a new instance of a static text control /// </summary> public StaticText(Dialog parent) : base(parent) { controlType = ControlType.StaticText; parentDialog = parent; textData = string.Empty; elementList.Clear(); } /// <summary> /// Render this control /// </summary> public override void Render(Device device, float elapsedTime) { if (!IsVisible) return; // Nothing to do here ControlState state = ControlState.Normal; if (!IsEnabled) state = ControlState.Disabled; // Blend the element colors Element e = elementList[TextElement] as Element; e.FontColor.Blend(state, elapsedTime); // Render with a shadow parentDialog.DrawText(textData, e, boundingBox, true); } /// <summary> /// Return a copy of the string /// </summary> public string GetTextCopy() { return textData.Clone() as string; } /// <summary> /// Sets the updated text for this control /// </summary> public void SetText(string newText) { textData = newText; } } #endregion #region Button control /// <summary> /// Button control /// </summary> public class Button : StaticText { public const int ButtonLayer = 0; public const int FillLayer = 1; protected bool isPressed; #region Event code public event EventHandler Click; /// <summary>Create new button instance</summary> protected void RaiseClickEvent(Button sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; if (Click != null) Click(sender, EventArgs.Empty); } #endregion /// <summary>Create new button instance</summary> public Button(Dialog parent) : base(parent) { controlType = ControlType.Button; parentDialog = parent; isPressed = false; hotKey = 0; } /// <summary>Can the button have focus</summary> public override bool CanHaveFocus { get { return IsVisible && IsEnabled; } } /// <summary>The hotkey for this button was pressed</summary> public override void OnHotKey() { RaiseClickEvent(this, true); } /// <summary> /// Will handle the keyboard strokes /// </summary> public override bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.KeyDown: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { isPressed = true; return true; } break; case NativeMethods.WindowMessage.KeyUp: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { isPressed = false; RaiseClickEvent(this, true); return true; } break; } return false; } /// <summary> /// Handle mouse messages from the buttons /// </summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { if (ContainsPoint(pt)) { // Pressed while inside the control isPressed = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); if (!hasFocus) Dialog.RequestFocus(this); return true; } } break; case NativeMethods.WindowMessage.LeftButtonUp: { if (isPressed) { isPressed = false; NativeMethods.ReleaseCapture(); if (!parentDialog.IsUsingKeyboardInput) Dialog.ClearFocus(); // Button click if (ContainsPoint(pt)) RaiseClickEvent(this, true); } } break; } return false; } /// <summary>Render the button</summary> public override void Render(Device device, float elapsedTime) { int offsetX = 0; int offsetY = 0; ControlState state = ControlState.Normal; if (IsVisible == false) { state = ControlState.Hidden; } else if (IsEnabled == false) { state = ControlState.Disabled; } else if (isPressed) { state = ControlState.Pressed; offsetX = 1; offsetY = 2; } else if (isMouseOver) { state = ControlState.MouseOver; offsetX = -1; offsetY = -2; } else if (hasFocus) { state = ControlState.Focus; } // Background fill layer Element e = elementList[Button.ButtonLayer] as Element; float blendRate = (state == ControlState.Pressed) ? 0.0f : 0.8f; System.Drawing.Rectangle buttonRect = boundingBox; buttonRect.Offset(offsetX, offsetY); // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); e.FontColor.Blend(state, elapsedTime, blendRate); // Draw sprite/text of button parentDialog.DrawSprite(e, buttonRect); parentDialog.DrawText(textData, e, buttonRect); // Main button e = elementList[Button.FillLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); e.FontColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, buttonRect); parentDialog.DrawText(textData, e, buttonRect); } } #endregion #region Checkbox Control /// <summary> /// Checkbox control /// </summary> public class Checkbox : Button { public const int BoxLayer = 0; public const int CheckLayer = 1; #region Event code public event EventHandler Changed; /// <summary>Create new button instance</summary> protected void RaiseChangedEvent(Checkbox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; // Fire both the changed and clicked event base.RaiseClickEvent(sender, wasTriggeredByUser); if (Changed != null) Changed(sender, EventArgs.Empty); } #endregion protected System.Drawing.Rectangle buttonRect; protected System.Drawing.Rectangle textRect; protected bool isBoxChecked; /// <summary> /// Create new checkbox instance /// </summary> public Checkbox(Dialog parent) : base(parent) { controlType = ControlType.CheckBox; isBoxChecked = false; parentDialog = parent; } /// <summary> /// Checked property /// </summary> public virtual bool IsChecked { get { return isBoxChecked; } set { SetCheckedInternal(value, false); } } /// <summary> /// Sets the checked state and fires the event if necessary /// </summary> protected virtual void SetCheckedInternal(bool ischecked, bool fromInput) { isBoxChecked = ischecked; RaiseChangedEvent(this, fromInput); } /// <summary> /// Override hotkey to fire event /// </summary> public override void OnHotKey() { SetCheckedInternal(!isBoxChecked, true); } /// <summary> /// Does the control contain the point? /// </summary> public override bool ContainsPoint(System.Drawing.Point pt) { return (boundingBox.Contains(pt) || buttonRect.Contains(pt)); } /// <summary> /// Update the rectangles /// </summary> protected override void UpdateRectangles() { // Update base first base.UpdateRectangles(); // Update the two rects buttonRect = boundingBox; buttonRect = new System.Drawing.Rectangle(boundingBox.Location, new System.Drawing.Size(boundingBox.Height, boundingBox.Height)); textRect = boundingBox; textRect.Offset((int) (1.25f * buttonRect.Width), 0); } /// <summary> /// Render the checkbox control /// </summary> public override void Render(Device device, float elapsedTime) { ControlState state = ControlState.Normal; if (IsVisible == false) state = ControlState.Hidden; else if (IsEnabled == false) state = ControlState.Disabled; else if (isPressed) state = ControlState.Pressed; else if (isMouseOver) state = ControlState.MouseOver; else if (hasFocus) state = ControlState.Focus; Element e = elementList[Checkbox.BoxLayer] as Element; float blendRate = (state == ControlState.Pressed) ? 0.0f : 0.8f; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); e.FontColor.Blend(state, elapsedTime, blendRate); // Draw sprite/text of checkbox parentDialog.DrawSprite(e, buttonRect); parentDialog.DrawText(textData, e, textRect); if (!isBoxChecked) state = ControlState.Hidden; e = elementList[Checkbox.CheckLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); // Draw sprite of checkbox parentDialog.DrawSprite(e, buttonRect); } /// <summary> /// Handle the keyboard for the checkbox /// </summary> public override bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.KeyDown: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { isPressed = true; return true; } break; case NativeMethods.WindowMessage.KeyUp: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { if (isPressed) { isPressed = false; SetCheckedInternal(!isBoxChecked, true); } return true; } break; } return false; } /// <summary> /// Handle mouse messages from the checkbox /// </summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { if (ContainsPoint(pt)) { // Pressed while inside the control isPressed = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); if ( (!hasFocus) && (parentDialog.IsUsingKeyboardInput) ) Dialog.RequestFocus(this); return true; } } break; case NativeMethods.WindowMessage.LeftButtonUp: { if (isPressed) { isPressed = false; NativeMethods.ReleaseCapture(); // Button click if (ContainsPoint(pt)) { SetCheckedInternal(!isBoxChecked, true); } return true; } } break; } return false; } } #endregion #region RadioButton Control /// <summary> /// Radio button control /// </summary> public class RadioButton : Checkbox { protected uint buttonGroupIndex; /// <summary> /// Create new radio button instance /// </summary> public RadioButton(Dialog parent) : base(parent) { controlType = ControlType.RadioButton; parentDialog = parent; } /// <summary> /// Button Group property /// </summary> public uint ButtonGroup { get { return buttonGroupIndex; } set { buttonGroupIndex = value; } } /// <summary> /// Sets the check state and potentially clears the group /// </summary> public void SetChecked(bool ischecked, bool clear) { SetCheckedInternal(ischecked, clear, false); } /// <summary> /// Sets the checked state and fires the event if necessary /// </summary> protected virtual void SetCheckedInternal(bool ischecked, bool clearGroup, bool fromInput) { isBoxChecked = ischecked; RaiseChangedEvent(this, fromInput); } /// <summary> /// Override hotkey to fire event /// </summary> public override void OnHotKey() { SetCheckedInternal(true, true); } /// <summary> /// Handle the keyboard for the checkbox /// </summary> public override bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.KeyDown: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { isPressed = true; return true; } break; case NativeMethods.WindowMessage.KeyUp: if ((System.Windows.Forms.Keys)wParam.ToInt32() == System.Windows.Forms.Keys.Space) { if (isPressed) { isPressed = false; parentDialog.ClearRadioButtonGroup(buttonGroupIndex); isBoxChecked = !isBoxChecked; RaiseChangedEvent(this, true); } return true; } break; } return false; } /// <summary> /// Handle mouse messages from the radio button /// </summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { if (ContainsPoint(pt)) { // Pressed while inside the control isPressed = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); if ( (!hasFocus) && (parentDialog.IsUsingKeyboardInput) ) Dialog.RequestFocus(this); return true; } } break; case NativeMethods.WindowMessage.LeftButtonUp: { if (isPressed) { isPressed = false; NativeMethods.ReleaseCapture(); // Button click if (ContainsPoint(pt)) { parentDialog.ClearRadioButtonGroup(buttonGroupIndex); isBoxChecked = !isBoxChecked; RaiseChangedEvent(this, true); } return true; } } break; } return false; } } #endregion #region Scrollbar control /// <summary> /// A scroll bar control /// </summary> public class ScrollBar : Control { public const int TrackLayer = 0; public const int UpButtonLayer = 1; public const int DownButtonLayer = 2; public const int ThumbLayer = 3; protected const int MinimumThumbSize = 8; #region Instance Data protected bool showingThumb; protected System.Drawing.Rectangle upButtonRect; protected System.Drawing.Rectangle downButtonRect; protected System.Drawing.Rectangle trackRect; protected System.Drawing.Rectangle thumbRect; protected int position; // Position of the first displayed item protected int pageSize; // How many items are displayable in one page protected int start; // First item protected int end; // The index after the last item private int thumbOffsetY; private bool isDragging; #endregion /// <summary> /// Creates a new instance of the scroll bar class /// </summary> public ScrollBar(Dialog parent) : base(parent) { // Store parent and control type controlType = ControlType.Scrollbar; parentDialog = parent; // Set default properties showingThumb = true; upButtonRect = System.Drawing.Rectangle.Empty; downButtonRect = System.Drawing.Rectangle.Empty; trackRect = System.Drawing.Rectangle.Empty; thumbRect = System.Drawing.Rectangle.Empty; position = 0; pageSize = 1; start = 0; end = 1; } /// <summary> /// Update all of the rectangles /// </summary> protected override void UpdateRectangles() { // Get the bounding box first base.UpdateRectangles(); // Make sure buttons are square upButtonRect = new System.Drawing.Rectangle(boundingBox.Location, new System.Drawing.Size(boundingBox.Width, boundingBox.Width)); downButtonRect = new System.Drawing.Rectangle(boundingBox.Left, boundingBox.Bottom - boundingBox.Width, boundingBox.Width, boundingBox.Width); trackRect = new System.Drawing.Rectangle(upButtonRect.Left, upButtonRect.Bottom, upButtonRect.Width, downButtonRect.Top - upButtonRect.Bottom); thumbRect = upButtonRect; UpdateThumbRectangle(); } /// <summary> /// Position of the track /// </summary> public int TrackPosition { get { return position; } set { position = value; Cap(); UpdateThumbRectangle(); } } /// <summary> /// Size of a 'page' /// </summary> public int PageSize { get { return pageSize; } set { pageSize = value; Cap(); UpdateThumbRectangle(); } } /// <summary>Clips position at boundaries</summary> protected void Cap() { if (position < start || end - start <= pageSize) { position = start; } else if (position + pageSize > end) position = end - pageSize; } /// <summary>Compute the dimension of the scroll thumb</summary> protected void UpdateThumbRectangle() { if (end - start > pageSize) { int thumbHeight = Math.Max(trackRect.Height * pageSize / (end-start), MinimumThumbSize); int maxPosition = end - start - pageSize; thumbRect.Location = new System.Drawing.Point(thumbRect.Left, trackRect.Top + (position - start) * (trackRect.Height - thumbHeight) / maxPosition); thumbRect.Size = new System.Drawing.Size(thumbRect.Width, thumbHeight); showingThumb = true; } else { // No content to scroll thumbRect.Height = 0; showingThumb = false; } } /// <summary>Scrolls by delta items. A positive value scrolls down, while a negative scrolls down</summary> public void Scroll(int delta) { // Perform scroll position += delta; // Cap position Cap(); // Update thumb rectangle UpdateThumbRectangle(); } /// <summary>Shows an item</summary> public void ShowItem(int index) { // Cap the index if (index < 0) index = 0; if (index >= end) index = end - 1; // Adjust the position to show this item if (position > index) position = index; else if (position + pageSize <= index) position = index - pageSize + 1; // Update thumbs again UpdateThumbRectangle(); } /// <summary>Sets the track range</summary> public void SetTrackRange(int startRange, int endRange) { start = startRange; end = endRange; Cap(); UpdateThumbRectangle(); } /// <summary>Render the scroll bar control</summary> public override void Render(Device device, float elapsedTime) { ControlState state = ControlState.Normal; if (IsVisible == false) state = ControlState.Hidden; else if ( (IsEnabled == false) || (showingThumb == false) ) state = ControlState.Disabled; else if (isMouseOver) state = ControlState.MouseOver; else if (hasFocus) state = ControlState.Focus; float blendRate = (state == ControlState.Pressed) ? 0.0f : 0.8f; // Background track layer Element e = elementList[ScrollBar.TrackLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, trackRect); // Up arrow e = elementList[ScrollBar.UpButtonLayer] as Element; e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, upButtonRect); // Down arrow e = elementList[ScrollBar.DownButtonLayer] as Element; e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, downButtonRect); // Thumb button e = elementList[ScrollBar.ThumbLayer] as Element; e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, thumbRect); } /// <summary>Stores data for a combo box item</summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { NativeMethods.SetCapture(Parent.SampleFramework.Window); // Check for on up button if (upButtonRect.Contains(pt)) { if (position > start) --position; UpdateThumbRectangle(); return true; } // Check for on down button if (downButtonRect.Contains(pt)) { if (position + pageSize < end) ++position; UpdateThumbRectangle(); return true; } // Check for click on thumb if (thumbRect.Contains(pt)) { isDragging = true; thumbOffsetY = pt.Y - thumbRect.Top; return true; } // check for click on track if (thumbRect.Left <= pt.X && thumbRect.Right > pt.X) { if (thumbRect.Top > pt.Y && trackRect.Top <= pt.Y) { Scroll(-(pageSize-1)); return true; } else if (thumbRect.Bottom <= pt.Y && trackRect.Bottom > pt.Y) { Scroll(pageSize-1); return true; } } break; } case NativeMethods.WindowMessage.LeftButtonUp: { isDragging = false; NativeMethods.ReleaseCapture(); UpdateThumbRectangle(); break; } case NativeMethods.WindowMessage.MouseMove: { if (isDragging) { // Calculate new bottom and top of thumb rect int bottom = thumbRect.Bottom + (pt.Y - thumbOffsetY - thumbRect.Top); int top = pt.Y - thumbOffsetY; thumbRect = new System.Drawing.Rectangle(thumbRect.Left, top, thumbRect.Width, bottom - top); if (thumbRect.Top < trackRect.Top) thumbRect.Offset(0, trackRect.Top - thumbRect.Top); else if (thumbRect.Bottom > trackRect.Bottom) thumbRect.Offset(0, trackRect.Bottom - thumbRect.Bottom); // Compute first item index based on thumb position int maxFirstItem = end - start - pageSize; // Largest possible index for first item int maxThumb = trackRect.Height - thumbRect.Height; // Largest possible thumb position position = start + (thumbRect.Top - trackRect.Top + maxThumb / (maxFirstItem * 2) ) * // Shift by half a row to avoid last row covered maxFirstItem / maxThumb; return true; } break; } } // Was not handled return false; } } #endregion #region ComboBox Control /// <summary>Stores data for a combo box item</summary> public struct ComboBoxItem { public string ItemText; public object ItemData; public System.Drawing.Rectangle ItemRect; public bool IsItemVisible; } /// <summary>Combo box control</summary> public class ComboBox : Button { public const int MainLayer = 0; public const int ComboButtonLayer = 1; public const int DropdownLayer = 2; public const int SelectionLayer = 3; #region Event code public event EventHandler Changed; /// <summary>Create new button instance</summary> protected void RaiseChangedEvent(ComboBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; // Fire both the changed and clicked event base.RaiseClickEvent(sender, wasTriggeredByUser); if (Changed != null) Changed(sender, EventArgs.Empty); } #endregion private bool isScrollBarInit; #region Instance data protected int selectedIndex; protected int focusedIndex; protected int dropHeight; protected ScrollBar scrollbarControl; protected int scrollWidth; protected bool isComboOpen; protected System.Drawing.Rectangle textRect; protected System.Drawing.Rectangle buttonRect; protected System.Drawing.Rectangle dropDownRect; protected System.Drawing.Rectangle dropDownTextRect; protected ArrayList itemList; #endregion /// <summary>Create new combo box control</summary> public ComboBox(Dialog parent) : base(parent) { // Store control type and parent dialog controlType = ControlType.ComboBox; parentDialog = parent; // Create the scrollbar control too scrollbarControl = new ScrollBar(parent); // Set some default items dropHeight = 100; scrollWidth = 16; selectedIndex = -1; focusedIndex = -1; isScrollBarInit = false; // Create the item list array itemList = new ArrayList(); } /// <summary>Update the rectangles for the combo box control</summary> protected override void UpdateRectangles() { // Get bounding box base.UpdateRectangles(); // Update the bounding box for the items buttonRect = new System.Drawing.Rectangle(boundingBox.Right - boundingBox.Height, boundingBox.Top, boundingBox.Height, boundingBox.Height); textRect = boundingBox; textRect.Size = new System.Drawing.Size(textRect.Width - buttonRect.Width, textRect.Height); dropDownRect = textRect; dropDownRect.Offset(0, (int)(0.9f * textRect.Height)); dropDownRect.Size = new System.Drawing.Size(dropDownRect.Width - scrollWidth, dropDownRect.Height + dropHeight); // Scale it down slightly System.Drawing.Point loc = dropDownRect.Location; System.Drawing.Size size = dropDownRect.Size; loc.X += (int)(0.1f * dropDownRect.Width); loc.Y += (int)(0.1f * dropDownRect.Height); size.Width -= (2 * (int)(0.1f * dropDownRect.Width)); size.Height -= (2 * (int)(0.1f * dropDownRect.Height)); dropDownTextRect = new System.Drawing.Rectangle(loc, size); // Update the scroll bars rects too scrollbarControl.SetLocation(dropDownRect.Right, dropDownRect.Top + 2); scrollbarControl.SetSize(scrollWidth, dropDownRect.Height - 2); FontNode fNode = DialogResourceManager.GetGlobalInstance().GetFontNode((int)(elementList[2] as Element).FontIndex); if ((fNode != null) && (fNode.Height > 0)) { scrollbarControl.PageSize = (int)(dropDownTextRect.Height / fNode.Height); // The selected item may have been scrolled off the page. // Ensure that it is in page again. scrollbarControl.ShowItem(selectedIndex); } } /// <summary>Sets the drop height of this control</summary> public void SetDropHeight(int height) { dropHeight = height; UpdateRectangles(); } /// <summary>Sets the scroll bar width of this control</summary> public void SetScrollbarWidth(int width) { scrollWidth = width; UpdateRectangles(); } /// <summary>Can this control have focus</summary> public override bool CanHaveFocus { get { return (IsVisible && IsEnabled); } } /// <summary>Number of items current in the list</summary> public int NumberItems { get { return itemList.Count; } } /// <summary>Indexer for items in the list</summary> public ComboBoxItem this[int index] { get { return (ComboBoxItem)itemList[index]; } } /// <summary>Initialize the scrollbar control here</summary> public override void OnInitialize() { parentDialog.InitializeControl(scrollbarControl); } /// <summary>Called when focus leaves the control</summary> public override void OnFocusOut() { // Call base first base.OnFocusOut (); isComboOpen = false; } /// <summary>Called when the control's hotkey is pressed</summary> public override void OnHotKey() { if (isComboOpen) return; // Nothing to do yet if (selectedIndex == -1) return; // Nothing selected selectedIndex++; if (selectedIndex >= itemList.Count) selectedIndex = 0; focusedIndex = selectedIndex; RaiseChangedEvent(this, true); } /// <summary>Called when the control needs to handle the keyboard</summary> public override bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { const uint RepeatMask = (0x40000000); if (!IsEnabled || !IsVisible) return false; // Let the scroll bar have a chance to handle it first if (scrollbarControl.HandleKeyboard(msg, wParam, lParam)) return true; switch (msg) { case NativeMethods.WindowMessage.KeyDown: { switch((System.Windows.Forms.Keys)wParam.ToInt32()) { case System.Windows.Forms.Keys.Return: { if (isComboOpen) { if (selectedIndex != focusedIndex) { selectedIndex = focusedIndex; RaiseChangedEvent(this, true); } isComboOpen = false; if (!Parent.IsUsingKeyboardInput) Dialog.ClearFocus(); return true; } break; } case System.Windows.Forms.Keys.F4: { // Filter out auto repeats if ((lParam.ToInt32() & RepeatMask) != 0) return true; isComboOpen = !isComboOpen; if (!isComboOpen) { RaiseChangedEvent(this, true); if (!Parent.IsUsingKeyboardInput) Dialog.ClearFocus(); } return true; } case System.Windows.Forms.Keys.Left: case System.Windows.Forms.Keys.Up: { if (focusedIndex > 0) { focusedIndex--; selectedIndex = focusedIndex; if (!isComboOpen) RaiseChangedEvent(this, true); } return true; } case System.Windows.Forms.Keys.Right: case System.Windows.Forms.Keys.Down: { if (focusedIndex + 1 < (int)NumberItems) { focusedIndex++; selectedIndex = focusedIndex; if (!isComboOpen) RaiseChangedEvent(this, true); } return true; } } break; } } return false; } /// <summary>Called when the control should handle the mouse</summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; // Nothing to do // Let the scroll bar handle it first if (scrollbarControl.HandleMouse(msg, pt, wParam, lParam)) return true; // Ok, scrollbar didn't handle it, move on switch(msg) { case NativeMethods.WindowMessage.MouseMove: { if (isComboOpen && dropDownRect.Contains(pt)) { // Determine which item has been selected for (int i = 0; i < itemList.Count; i++) { ComboBoxItem cbi = (ComboBoxItem)itemList[i]; if (cbi.IsItemVisible && cbi.ItemRect.Contains(pt)) { focusedIndex = i; } } return true; } break; } case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { if (ContainsPoint(pt)) { // Pressed while inside the control isPressed = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); if (!hasFocus) Dialog.RequestFocus(this); // Toggle dropdown if (hasFocus) { isComboOpen = !isComboOpen; if (!isComboOpen) { if (!parentDialog.IsUsingKeyboardInput) Dialog.ClearFocus(); } } return true; } // Perhaps this click is within the dropdown if (isComboOpen && dropDownRect.Contains(pt)) { // Determine which item has been selected for (int i = scrollbarControl.TrackPosition; i < itemList.Count; i++) { ComboBoxItem cbi = (ComboBoxItem)itemList[i]; if (cbi.IsItemVisible && cbi.ItemRect.Contains(pt)) { selectedIndex = focusedIndex = i; RaiseChangedEvent(this, true); isComboOpen = false; if (!parentDialog.IsUsingKeyboardInput) Dialog.ClearFocus(); break; } } return true; } // Mouse click not on main control or in dropdown, fire an event if needed if (isComboOpen) { focusedIndex = selectedIndex; RaiseChangedEvent(this, true); isComboOpen = false; } // Make sure the control is no longer 'pressed' isPressed = false; // Release focus if appropriate if (!parentDialog.IsUsingKeyboardInput) Dialog.ClearFocus(); break; } case NativeMethods.WindowMessage.LeftButtonUp: { if (isPressed && ContainsPoint(pt)) { // Button click isPressed = false; NativeMethods.ReleaseCapture(); return true; } break; } case NativeMethods.WindowMessage.MouseWheel: { int zdelta = (short)NativeMethods.HiWord((uint)wParam.ToInt32()) / Dialog.WheelDelta; if (isComboOpen) { scrollbarControl.Scroll(-zdelta * System.Windows.Forms.SystemInformation.MouseWheelScrollLines); } else { if (zdelta > 0) { if (focusedIndex > 0) { focusedIndex--; selectedIndex = focusedIndex; if (!isComboOpen) { RaiseChangedEvent(this, true); } } } else { if (focusedIndex +1 < NumberItems) { focusedIndex++; selectedIndex = focusedIndex; if (!isComboOpen) { RaiseChangedEvent(this, true); } } } } return true; } } // Didn't handle it return false; } /// <summary>Called when the control should be rendered</summary> public override void Render(Device device, float elapsedTime) { ControlState state = ControlState.Normal; if (!isComboOpen) state = ControlState.Hidden; // Dropdown box Element e = elementList[ComboBox.DropdownLayer] as Element; // If we have not initialized the scroll bar page size, // do that now. if (!isScrollBarInit) { FontNode fNode = DialogResourceManager.GetGlobalInstance().GetFontNode((int)e.FontIndex); if ((fNode != null) && (fNode.Height > 0)) scrollbarControl.PageSize = (int)(dropDownTextRect.Height / fNode.Height); else scrollbarControl.PageSize = dropDownTextRect.Height; isScrollBarInit = true; } if (isComboOpen) scrollbarControl.Render(device, elapsedTime); // Blend current color e.TextureColor.Blend(state, elapsedTime); e.FontColor.Blend(state, elapsedTime); parentDialog.DrawSprite(e, dropDownRect); // Selection outline Element selectionElement = elementList[ComboBox.SelectionLayer] as Element; selectionElement.TextureColor.Current = e.TextureColor.Current; selectionElement.FontColor.Current = selectionElement.FontColor.States[(int)ControlState.Normal]; FontNode font = DialogResourceManager.GetGlobalInstance().GetFontNode((int)e.FontIndex); int currentY = dropDownTextRect.Top; int remainingHeight = dropDownTextRect.Height; for (int i = scrollbarControl.TrackPosition; i < itemList.Count; i++) { ComboBoxItem cbi = (ComboBoxItem)itemList[i]; // Make sure there's room left in the dropdown remainingHeight -= (int)font.Height; if (remainingHeight < 0) { // Not visible, store that item cbi.IsItemVisible = false; itemList[i] = cbi; // Store this back in list continue; } cbi.ItemRect = new System.Drawing.Rectangle(dropDownTextRect.Left, currentY, dropDownTextRect.Width, (int)font.Height); cbi.IsItemVisible = true; currentY += (int)font.Height; itemList[i] = cbi; // Store this back in list if (isComboOpen) { if (focusedIndex == i) { System.Drawing.Rectangle rect = new System.Drawing.Rectangle( dropDownRect.Left, cbi.ItemRect.Top - 2, dropDownRect.Width, cbi.ItemRect.Height + 4); parentDialog.DrawSprite(selectionElement, rect); parentDialog.DrawText(cbi.ItemText, selectionElement, cbi.ItemRect); } else { parentDialog.DrawText(cbi.ItemText, e, cbi.ItemRect); } } } int offsetX = 0; int offsetY = 0; state = ControlState.Normal; if (IsVisible == false) state = ControlState.Hidden; else if (IsEnabled == false) state = ControlState.Disabled; else if (isPressed) { state = ControlState.Pressed; offsetX = 1; offsetY = 2; } else if (isMouseOver) { state = ControlState.MouseOver; offsetX = -1; offsetY = -2; } else if (hasFocus) state = ControlState.Focus; float blendRate = (state == ControlState.Pressed) ? 0.0f : 0.8f; // Button e = elementList[ComboBox.ComboButtonLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); System.Drawing.Rectangle windowRect = buttonRect; windowRect.Offset(offsetX, offsetY); // Draw sprite parentDialog.DrawSprite(e, windowRect); if (isComboOpen) state = ControlState.Pressed; // Main text box e = elementList[ComboBox.MainLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); e.FontColor.Blend(state, elapsedTime, blendRate); // Draw sprite parentDialog.DrawSprite(e, textRect); if (selectedIndex >= 0 && selectedIndex < itemList.Count) { try { ComboBoxItem cbi = (ComboBoxItem)itemList[selectedIndex]; parentDialog.DrawText(cbi.ItemText, e, textRect); } catch {} // Ignore } } #region Item Controlling methods /// <summary>Adds an item to the combo box control</summary> public void AddItem(string text, object data) { if ((text == null) || (text.Length == 0)) throw new ArgumentNullException("text", "You must pass in a valid item name when adding a new item."); // Create a new item and add it ComboBoxItem newitem = new ComboBoxItem(); newitem.ItemText = text; newitem.ItemData = data; itemList.Add(newitem); // Update the scrollbar with the new range scrollbarControl.SetTrackRange(0, itemList.Count); // If this is the only item in the list, it should be selected if (NumberItems == 1) { selectedIndex = 0; focusedIndex = 0; RaiseChangedEvent(this, false); } } /// <summary>Removes an item at a particular index</summary> public void RemoveAt(int index) { // Remove the item itemList.RemoveAt(index); // Update the scrollbar with the new range scrollbarControl.SetTrackRange(0, itemList.Count); if (selectedIndex >= itemList.Count) selectedIndex = itemList.Count - 1; } /// <summary>Removes all items from the control</summary> public void Clear() { // clear the list itemList.Clear(); // Update scroll bar and index scrollbarControl.SetTrackRange(0, 1); focusedIndex = selectedIndex = -1; } /// <summary>Determines whether this control contains an item</summary> public bool ContainsItem(string text, int start) { return (FindItem(text, start) != -1); } /// <summary>Determines whether this control contains an item</summary> public bool ContainsItem(string text) { return ContainsItem(text, 0); } /// <summary>Gets the data for the selected item</summary> public object GetSelectedData() { if (selectedIndex < 0) return null; // Nothing selected ComboBoxItem cbi = (ComboBoxItem)itemList[selectedIndex]; return cbi.ItemData; } /// <summary>Gets the selected item</summary> public ComboBoxItem GetSelectedItem() { if (selectedIndex < 0) throw new ArgumentOutOfRangeException("selectedIndex", "No item selected."); return (ComboBoxItem)itemList[selectedIndex]; } /// <summary>Gets the data for an item</summary> public object GetItemData(string text) { int index = FindItem(text); if (index == -1) return null; // no item ComboBoxItem cbi = (ComboBoxItem)itemList[index]; return cbi.ItemData; } /// <summary>Finds an item in the list and returns the index</summary> protected int FindItem(string text, int start) { if ((text == null) || (text.Length == 0)) return -1; for(int i = start; i < itemList.Count; i++) { ComboBoxItem cbi = (ComboBoxItem)itemList[i]; if (string.Compare(cbi.ItemText, text, true) == 0) { return i; } } // Never found it return -1; } /// <summary>Finds an item in the list and returns the index</summary> protected int FindItem(string text) { return FindItem(text, 0); } /// <summary>Sets the selected item by index</summary> public void SetSelected(int index) { if (index >= NumberItems) throw new ArgumentOutOfRangeException("index", "There are not enough items in the list to select this index."); focusedIndex = selectedIndex = index; RaiseChangedEvent(this, false); } /// <summary>Sets the selected item by text</summary> public void SetSelected(string text) { if ((text == null) || (text.Length == 0)) throw new ArgumentNullException("text", "You must pass in a valid item name when adding a new item."); int index = FindItem(text); if (index == -1) throw new InvalidOperationException("This item could not be found."); focusedIndex = selectedIndex = index; RaiseChangedEvent(this, false); } /// <summary>Sets the selected item by data</summary> public void SetSelectedByData(object data) { for (int index = 0; index < itemList.Count; index++) { ComboBoxItem cbi = (ComboBoxItem)itemList[index]; if (cbi.ItemData.ToString().Equals(data.ToString())) { focusedIndex = selectedIndex = index; RaiseChangedEvent(this, false); } } // Could not find this item. Uncomment line below for debug information //System.Diagnostics.Debugger.Log(9,string.Empty, "Could not find an object with this data.\r\n"); } #endregion } #endregion #region Slider Control /// <summary>Slider control</summary> public class Slider : Control { public const int TrackLayer = 0; public const int ButtonLayer = 1; #region Instance Data public event EventHandler ValueChanged; protected int currentValue; protected int maxValue; protected int minValue; protected int dragX; // Mouse position at the start of the drag protected int dragOffset; // Drag offset from the center of the button protected int buttonX; protected bool isPressed; protected System.Drawing.Rectangle buttonRect; /// <summary>Slider's can always have focus</summary> public override bool CanHaveFocus { get { return true; }} /// <summary>Current value of the slider</summary> protected void RaiseValueChanged(Slider sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; if (ValueChanged != null) ValueChanged(sender, EventArgs.Empty); } /// <summary>Current value of the slider</summary> public int Value { get { return currentValue; } set { SetValueInternal(value, false); } } /// <summary>Sets the range of the slider</summary> public void SetRange(int min, int max) { minValue = min; maxValue = max; SetValueInternal(currentValue, false); } /// <summary>Sets the value internally and fires the event if needed</summary> protected void SetValueInternal(int newValue, bool fromInput) { // Clamp to the range newValue = Math.Max(minValue, newValue); newValue = Math.Min(maxValue, newValue); if (newValue == currentValue) return; // Update the value, the rects, then fire the events if necessar currentValue = newValue; UpdateRectangles(); RaiseValueChanged(this, fromInput); } #endregion /// <summary>Create new button instance</summary> public Slider(Dialog parent): base(parent) { controlType = ControlType.Slider; parentDialog = parent; isPressed = false; minValue = 0; maxValue = 100; currentValue = 50; } /// <summary>Does the control contain this point?</summary> public override bool ContainsPoint(System.Drawing.Point pt) { return boundingBox.Contains(pt) || buttonRect.Contains(pt); } /// <summary>Update the rectangles for the control</summary> protected override void UpdateRectangles() { // First get the bounding box base.UpdateRectangles (); // Create the button rect buttonRect = boundingBox; buttonRect.Width = buttonRect.Height; // Make it square // Offset it buttonRect.Offset(-buttonRect.Width / 2, 0); buttonX = (int)((currentValue - minValue) * (float)boundingBox.Width / (maxValue - minValue) ); buttonRect.Offset(buttonX, 0); } /// <summary>Gets a value from a position</summary> public int ValueFromPosition(int x) { float valuePerPixel = ((float)(maxValue - minValue) / (float)boundingBox.Width); return (int)(0.5f + minValue + valuePerPixel * (x - boundingBox.Left)); } /// <summary>Handle mouse input input</summary> public override bool HandleMouse(Microsoft.Samples.DirectX.UtilityToolkit.NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { if (buttonRect.Contains(pt)) { // Pressed while inside the control isPressed = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); dragX = pt.X; dragOffset = buttonX - dragX; if (!hasFocus) Dialog.RequestFocus(this); return true; } if (boundingBox.Contains(pt)) { if (pt.X > buttonX + controlX) { SetValueInternal(currentValue + 1, true); return true; } if (pt.X < buttonX + controlX) { SetValueInternal(currentValue - 1, true); return true; } } break; } case NativeMethods.WindowMessage.LeftButtonUp: { if (isPressed) { isPressed = false; NativeMethods.ReleaseCapture(); Dialog.ClearFocus(); RaiseValueChanged(this, true); return true; } break; } case NativeMethods.WindowMessage.MouseMove: { if (isPressed) { SetValueInternal(ValueFromPosition(controlX + pt.X + dragOffset), true); return true; } break; } } return false; } /// <summary>Handle keyboard input</summary> public override bool HandleKeyboard(Microsoft.Samples.DirectX.UtilityToolkit.NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; if (msg == NativeMethods.WindowMessage.KeyDown) { switch((System.Windows.Forms.Keys)wParam.ToInt32()) { case System.Windows.Forms.Keys.Home: SetValueInternal(minValue, true); return true; case System.Windows.Forms.Keys.End: SetValueInternal(maxValue, true); return true; case System.Windows.Forms.Keys.Prior: case System.Windows.Forms.Keys.Left: case System.Windows.Forms.Keys.Up: SetValueInternal(currentValue - 1, true); return true; case System.Windows.Forms.Keys.Next: case System.Windows.Forms.Keys.Right: case System.Windows.Forms.Keys.Down: SetValueInternal(currentValue + 1, true); return true; } } return false; } /// <summary>Render the slider</summary> public override void Render(Device device, float elapsedTime) { ControlState state = ControlState.Normal; if (IsVisible == false) { state = ControlState.Hidden; } else if (IsEnabled == false) { state = ControlState.Disabled; } else if (isPressed) { state = ControlState.Pressed; } else if (isMouseOver) { state = ControlState.MouseOver; } else if (hasFocus) { state = ControlState.Focus; } float blendRate = (state == ControlState.Pressed) ? 0.0f : 0.8f; Element e = elementList[Slider.TrackLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, boundingBox); e = elementList[Slider.ButtonLayer] as Element; // Blend current color e.TextureColor.Blend(state, elapsedTime, blendRate); parentDialog.DrawSprite(e, buttonRect); } } #endregion #region Listbox Control /// <summary>Style of the list box</summary> public enum ListBoxStyle { SingleSelection, Multiselection, } /// <summary>Stores data for a list box item</summary> public struct ListBoxItem { public string ItemText; public object ItemData; public System.Drawing.Rectangle ItemRect; public bool IsItemSelected; } /// <summary>List box control</summary> public class ListBox : Control { public const int MainLayer = 0; public const int SelectionLayer = 1; #region Event code public event EventHandler ContentsChanged; public event EventHandler DoubleClick; public event EventHandler Selection; /// <summary>Raises the contents changed event</summary> protected void RaiseContentsChangedEvent(ListBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; // Fire the event if (ContentsChanged != null) ContentsChanged(sender, EventArgs.Empty); } /// <summary>Raises the double click event</summary> protected void RaiseDoubleClickEvent(ListBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; // Fire the event if (DoubleClick != null) DoubleClick(sender, EventArgs.Empty); } /// <summary>Raises the selection event</summary> protected void RaiseSelectionEvent(ListBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; // Fire the event if (Selection != null) Selection(sender, EventArgs.Empty); } #endregion #region Instance data private bool isScrollBarInit; protected System.Drawing.Rectangle textRect; // Text rendering bound protected System.Drawing.Rectangle selectionRect; // Selection box bound protected ScrollBar scrollbarControl; protected int scrollWidth; protected int border; protected int margin; protected int textHeight; // Height of a single line of text protected int selectedIndex; protected int selectedStarted; protected bool isDragging; protected ListBoxStyle style; protected ArrayList itemList; #endregion /// <summary>Create a new list box control</summary> public ListBox(Dialog parent) : base(parent) { // Store control type and parent dialog controlType = ControlType.ListBox; parentDialog = parent; // Create the scrollbar control too scrollbarControl = new ScrollBar(parent); // Set some default items style = ListBoxStyle.SingleSelection; scrollWidth = 16; selectedIndex = -1; selectedStarted = 0; isDragging = false; margin = 5; border = 6; textHeight = 0; isScrollBarInit = false; // Create the item list array itemList = new ArrayList(); } /// <summary>Update the rectangles for the list box control</summary> protected override void UpdateRectangles() { // Get bounding box base.UpdateRectangles(); // Calculate the size of the selection rectangle selectionRect = boundingBox; selectionRect.Width -= scrollWidth; selectionRect.Inflate(-border, -border); textRect = selectionRect; textRect.Inflate(-margin, 0); // Update the scroll bars rects too scrollbarControl.SetLocation(boundingBox.Right - scrollWidth, boundingBox.Top); scrollbarControl.SetSize(scrollWidth, height); FontNode fNode = DialogResourceManager.GetGlobalInstance().GetFontNode((int)(elementList[0] as Element).FontIndex); if ((fNode != null) && (fNode.Height > 0)) { scrollbarControl.PageSize = (int)(textRect.Height / fNode.Height); // The selected item may have been scrolled off the page. // Ensure that it is in page again. scrollbarControl.ShowItem(selectedIndex); } } /// <summary>Sets the scroll bar width of this control</summary> public void SetScrollbarWidth(int width) { scrollWidth = width; UpdateRectangles(); } /// <summary>Can this control have focus</summary> public override bool CanHaveFocus { get { return (IsVisible && IsEnabled); } } /// <summary>Sets the style of the listbox</summary> public ListBoxStyle Style { get { return style; } set { style = value; } } /// <summary>Number of items current in the list</summary> public int NumberItems { get { return itemList.Count; } } /// <summary>Indexer for items in the list</summary> public ListBoxItem this[int index] { get { return (ListBoxItem)itemList[index]; } } /// <summary>Initialize the scrollbar control here</summary> public override void OnInitialize() { parentDialog.InitializeControl(scrollbarControl); } /// <summary>Called when the control needs to handle the keyboard</summary> public override bool HandleKeyboard(NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; // Let the scroll bar have a chance to handle it first if (scrollbarControl.HandleKeyboard(msg, wParam, lParam)) return true; switch (msg) { case NativeMethods.WindowMessage.KeyDown: { switch((System.Windows.Forms.Keys)wParam.ToInt32()) { case System.Windows.Forms.Keys.Up: case System.Windows.Forms.Keys.Down: case System.Windows.Forms.Keys.Next: case System.Windows.Forms.Keys.Prior: case System.Windows.Forms.Keys.Home: case System.Windows.Forms.Keys.End: { // If no items exists, do nothing if (itemList.Count == 0) return true; int oldSelected = selectedIndex; // Adjust selectedIndex switch((System.Windows.Forms.Keys)wParam.ToInt32()) { case System.Windows.Forms.Keys.Up: --selectedIndex; break; case System.Windows.Forms.Keys.Down: ++selectedIndex; break; case System.Windows.Forms.Keys.Next: selectedIndex += scrollbarControl.PageSize - 1; break; case System.Windows.Forms.Keys.Prior: selectedIndex -= scrollbarControl.PageSize - 1; break; case System.Windows.Forms.Keys.Home: selectedIndex = 0; break; case System.Windows.Forms.Keys.End: selectedIndex = itemList.Count - 1; break; } // Clamp the item if (selectedIndex < 0) selectedIndex = 0; if (selectedIndex >= itemList.Count) selectedIndex = itemList.Count - 1; // Did the selection change? if (oldSelected != selectedIndex) { if (style == ListBoxStyle.Multiselection) { // Clear all selection for(int i = 0; i < itemList.Count; i++) { ListBoxItem lbi = (ListBoxItem)itemList[i]; lbi.IsItemSelected = false; itemList[i] = lbi; } // Is shift being held down? bool shiftDown = ((NativeMethods.GetAsyncKeyState ((int)System.Windows.Forms.Keys.ShiftKey) & 0x8000) != 0); if (shiftDown) { // Select all items from the start selection to current selected index int end = Math.Max(selectedStarted, selectedIndex); for(int i = Math.Min(selectedStarted, selectedIndex); i <= end; ++i) { ListBoxItem lbi = (ListBoxItem)itemList[i]; lbi.IsItemSelected = true; itemList[i] = lbi; } } else { ListBoxItem lbi = (ListBoxItem)itemList[selectedIndex]; lbi.IsItemSelected = true; itemList[selectedIndex] = lbi; // Update selection start selectedStarted = selectedIndex; } } else // Update selection start selectedStarted = selectedIndex; // adjust scrollbar scrollbarControl.ShowItem(selectedIndex); RaiseSelectionEvent(this, true); } } return true; } break; } } return false; } /// <summary>Called when the control should handle the mouse</summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { const int ShiftModifier = 0x0004; const int ControlModifier = 0x0008; if (!IsEnabled || !IsVisible) return false; // Nothing to do // First acquire focus if (msg == NativeMethods.WindowMessage.LeftButtonDown) if (!hasFocus) Dialog.RequestFocus(this); // Let the scroll bar handle it first if (scrollbarControl.HandleMouse(msg, pt, wParam, lParam)) return true; // Ok, scrollbar didn't handle it, move on switch(msg) { case NativeMethods.WindowMessage.LeftButtonDoubleClick: case NativeMethods.WindowMessage.LeftButtonDown: { // Check for clicks in the text area if (itemList.Count > 0 && selectionRect.Contains(pt)) { // Compute the index of the clicked item int clicked = 0; if (textHeight > 0) clicked = scrollbarControl.TrackPosition + (pt.Y - textRect.Top) / textHeight; else clicked = -1; // Only proceed if the click falls ontop of an item if (clicked >= scrollbarControl.TrackPosition && clicked < itemList.Count && clicked < scrollbarControl.TrackPosition + scrollbarControl.PageSize ) { NativeMethods.SetCapture(Parent.SampleFramework.Window); isDragging = true; // If this is a double click, fire off an event and exit // since the first click would have taken care of the selection // updating. if (msg == NativeMethods.WindowMessage.LeftButtonDoubleClick) { RaiseDoubleClickEvent(this, true); return true; } selectedIndex = clicked; if ( (wParam.ToInt32() & ShiftModifier) == 0) selectedStarted = selectedIndex; // Shift isn't down // If this is a multi-selection listbox, update per-item // selection data. if (style == ListBoxStyle.Multiselection) { // Determine behavior based on the state of Shift and Ctrl ListBoxItem selectedItem = (ListBoxItem)itemList[selectedIndex]; if ((wParam.ToInt32() & (ShiftModifier | ControlModifier)) == ControlModifier) { // Control click, reverse the selection selectedItem.IsItemSelected = !selectedItem.IsItemSelected; itemList[selectedIndex] = selectedItem; } else if ((wParam.ToInt32() & (ShiftModifier | ControlModifier)) == ShiftModifier) { // Shift click. Set the selection for all items // from last selected item to the current item. // Clear everything else. int begin = Math.Min(selectedStarted, selectedIndex); int end = Math.Max(selectedStarted, selectedIndex); // Unselect everthing before the beginning for(int i = 0; i < begin; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = false; itemList[i] = lb; } // unselect everything after the end for(int i = end + 1; i < itemList.Count; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = false; itemList[i] = lb; } // Select everything between for(int i = begin; i <= end; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = true; itemList[i] = lb; } } else if ((wParam.ToInt32() & (ShiftModifier | ControlModifier)) == (ShiftModifier | ControlModifier)) { // Control-Shift-click. // The behavior is: // Set all items from selectedStarted to selectedIndex to // the same state as selectedStarted, not including selectedIndex. // Set selectedIndex to selected. int begin = Math.Min(selectedStarted, selectedIndex); int end = Math.Max(selectedStarted, selectedIndex); // The two ends do not need to be set here. bool isLastSelected = ((ListBoxItem)itemList[selectedStarted]).IsItemSelected; for (int i = begin + 1; i < end; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = isLastSelected; itemList[i] = lb; } selectedItem.IsItemSelected = true; itemList[selectedIndex] = selectedItem; // Restore selectedIndex to the previous value // This matches the Windows behavior selectedIndex = selectedStarted; } else { // Simple click. Clear all items and select the clicked // item. for(int i = 0; i < itemList.Count; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = false; itemList[i] = lb; } selectedItem.IsItemSelected = true; itemList[selectedIndex] = selectedItem; } } // End of multi-selection case RaiseSelectionEvent(this, true); } return true; } break; } case NativeMethods.WindowMessage.LeftButtonUp: { NativeMethods.ReleaseCapture(); isDragging = false; if (selectedIndex != -1) { // Set all items between selectedStarted and selectedIndex to // the same state as selectedStarted int end = Math.Max(selectedStarted, selectedIndex); for (int i = Math.Min(selectedStarted, selectedIndex) + 1; i < end; ++i) { ListBoxItem lb = (ListBoxItem)itemList[i]; lb.IsItemSelected = ((ListBoxItem)itemList[selectedStarted]).IsItemSelected; itemList[i] = lb; } ListBoxItem lbs = (ListBoxItem)itemList[selectedIndex]; lbs.IsItemSelected = ((ListBoxItem)itemList[selectedStarted]).IsItemSelected; itemList[selectedIndex] = lbs; // If selectedStarted and selectedIndex are not the same, // the user has dragged the mouse to make a selection. // Notify the application of this. if (selectedIndex != selectedStarted) RaiseSelectionEvent(this, true); } break; } case NativeMethods.WindowMessage.MouseWheel: { int lines = System.Windows.Forms.SystemInformation.MouseWheelScrollLines; int scrollAmount = (int)(NativeMethods.HiWord((uint)wParam.ToInt32()) / Dialog.WheelDelta * lines); scrollbarControl.Scroll(-scrollAmount); break; } case NativeMethods.WindowMessage.MouseMove: { if (isDragging) { // compute the index of the item below the cursor int itemIndex = -1; if (textHeight > 0) itemIndex = scrollbarControl.TrackPosition + (pt.Y - textRect.Top) / textHeight; // Only proceed if the cursor is on top of an item if (itemIndex >= scrollbarControl.TrackPosition && itemIndex < itemList.Count && itemIndex < scrollbarControl.TrackPosition + scrollbarControl.PageSize) { selectedIndex = itemIndex; RaiseSelectionEvent(this, true); } else if (itemIndex < scrollbarControl.TrackPosition) { // User drags the mouse above window top scrollbarControl.Scroll(-1); selectedIndex = scrollbarControl.TrackPosition; RaiseSelectionEvent(this, true); } else if (itemIndex >= scrollbarControl.TrackPosition + scrollbarControl.PageSize) { // User drags the mouse below the window bottom scrollbarControl.Scroll(1); selectedIndex = Math.Min(itemList.Count, scrollbarControl.TrackPosition + scrollbarControl.PageSize - 1); RaiseSelectionEvent(this, true); } } break; } } // Didn't handle it return false; } /// <summary>Called when the control should be rendered</summary> public override void Render(Device device, float elapsedTime) { if (!IsVisible) return; // Nothing to render Element e = elementList[ListBox.MainLayer] as Element; // Blend current color e.TextureColor.Blend(ControlState.Normal, elapsedTime); e.FontColor.Blend(ControlState.Normal, elapsedTime); Element selectedElement = elementList[ListBox.SelectionLayer] as Element; // Blend current color selectedElement.TextureColor.Blend(ControlState.Normal, elapsedTime); selectedElement.FontColor.Blend(ControlState.Normal, elapsedTime); parentDialog.DrawSprite(e, boundingBox); // Render the text if (itemList.Count > 0) { // Find out the height of a single line of text System.Drawing.Rectangle rc = textRect; System.Drawing.Rectangle sel = selectionRect; rc.Height = (int)(DialogResourceManager.GetGlobalInstance().GetFontNode((int)e.FontIndex).Height); textHeight = rc.Height; // If we have not initialized the scroll bar page size, // do that now. if (!isScrollBarInit) { if (textHeight > 0) scrollbarControl.PageSize = (int)(textRect.Height / textHeight); else scrollbarControl.PageSize = textRect.Height; isScrollBarInit = true; } rc.Width = textRect.Width; for (int i = scrollbarControl.TrackPosition; i < itemList.Count; ++i) { if (rc.Bottom > textRect.Bottom) break; ListBoxItem lb = (ListBoxItem)itemList[i]; // Determine if we need to render this item with the // selected element. bool isSelectedStyle = false; if ( (selectedIndex == i) && (style == ListBoxStyle.SingleSelection) ) isSelectedStyle = true; else if (style == ListBoxStyle.Multiselection) { if (isDragging && ( ( i >= selectedIndex && i < selectedStarted) || (i <= selectedIndex && i > selectedStarted) ) ) { ListBoxItem selStart = (ListBoxItem)itemList[selectedStarted]; isSelectedStyle = selStart.IsItemSelected; } else isSelectedStyle = lb.IsItemSelected; } // Now render the text if (isSelectedStyle) { sel.Location = new System.Drawing.Point(sel.Left, rc.Top); sel.Height = rc.Height; parentDialog.DrawSprite(selectedElement, sel); parentDialog.DrawText(lb.ItemText, selectedElement, rc); } else parentDialog.DrawText(lb.ItemText, e, rc); rc.Offset(0, textHeight); } } // Render the scrollbar finally scrollbarControl.Render(device, elapsedTime); } #region Item Controlling methods /// <summary>Adds an item to the list box control</summary> public void AddItem(string text, object data) { if ((text == null) || (text.Length == 0)) throw new ArgumentNullException("text", "You must pass in a valid item name when adding a new item."); // Create a new item and add it ListBoxItem newitem = new ListBoxItem(); newitem.ItemText = text; newitem.ItemData = data; newitem.IsItemSelected = false; itemList.Add(newitem); // Update the scrollbar with the new range scrollbarControl.SetTrackRange(0, itemList.Count); } /// <summary>Inserts an item to the list box control</summary> public void InsertItem(int index, string text, object data) { if ((text == null) || (text.Length == 0)) throw new ArgumentNullException("text", "You must pass in a valid item name when adding a new item."); // Create a new item and insert it ListBoxItem newitem = new ListBoxItem(); newitem.ItemText = text; newitem.ItemData = data; newitem.IsItemSelected = false; itemList.Insert(index, newitem); // Update the scrollbar with the new range scrollbarControl.SetTrackRange(0, itemList.Count); } /// <summary>Removes an item at a particular index</summary> public void RemoveAt(int index) { // Remove the item itemList.RemoveAt(index); // Update the scrollbar with the new range scrollbarControl.SetTrackRange(0, itemList.Count); if (selectedIndex >= itemList.Count) selectedIndex = itemList.Count - 1; RaiseSelectionEvent(this, true); } /// <summary>Removes all items from the control</summary> public void Clear() { // clear the list itemList.Clear(); // Update scroll bar and index scrollbarControl.SetTrackRange(0, 1); selectedIndex = -1; } /// <summary> /// For single-selection listbox, returns the index of the selected item. /// For multi-selection, returns the first selected item after the previousSelected position. /// To search for the first selected item, the app passes -1 for previousSelected. For /// subsequent searches, the app passes the returned index back to GetSelectedIndex as. /// previousSelected. /// Returns -1 on error or if no item is selected. /// </summary> public int GetSelectedIndex(int previousSelected) { if (previousSelected < -1) return -1; if (style == ListBoxStyle.Multiselection) { // Multiple selections enabled. Search for the next item with the selected flag for (int i = previousSelected + 1; i < itemList.Count; ++i) { ListBoxItem lbi = (ListBoxItem)itemList[i]; if (lbi.IsItemSelected) return i; } return -1; } else { // Single selection return selectedIndex; } } /// <summary>Gets the selected item</summary> public ListBoxItem GetSelectedItem(int previousSelected) { return (ListBoxItem)itemList[GetSelectedIndex(previousSelected)]; } /// <summary>Gets the selected item</summary> public ListBoxItem GetSelectedItem() { return GetSelectedItem(-1); } /// <summary>Sets the border and margin sizes</summary> public void SetBorder(int borderSize, int marginSize) { border = borderSize; margin = marginSize; UpdateRectangles(); } /// <summary>Selects this item</summary> public void SelectItem(int newIndex) { if (itemList.Count == 0) return; // If no items exist there's nothing to do int oldSelected = selectedIndex; // Select the new item selectedIndex = newIndex; // Clamp the item if (selectedIndex < 0) selectedIndex = 0; if (selectedIndex > itemList.Count) selectedIndex = itemList.Count - 1; // Did the selection change? if (oldSelected != selectedIndex) { if (style == ListBoxStyle.Multiselection) { ListBoxItem lbi = (ListBoxItem)itemList[selectedIndex]; lbi.IsItemSelected = true; itemList[selectedIndex] = lbi; } // Update selection start selectedStarted = selectedIndex; // adjust scrollbar scrollbarControl.ShowItem(selectedIndex); } RaiseSelectionEvent(this, true); } #endregion } #endregion /// <summary>A basic edit box</summary> public class EditBox : Control { #region Element layers public const int TextLayer = 0; public const int TopLeftBorder = 1; public const int TopBorder = 2; public const int TopRightBorder = 3; public const int LeftBorder = 4; public const int RightBorder = 5; public const int LowerLeftBorder = 6; public const int LowerBorder = 7; public const int LowerRightBorder = 8; #endregion #region Event code public event EventHandler Changed; public event EventHandler Enter; /// <summary>Raises the changed event</summary> protected void RaiseChangedEvent(EditBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; if (Changed != null) Changed(sender, EventArgs.Empty); } /// <summary>Raises the Enter event</summary> protected void RaiseEnterEvent(EditBox sender, bool wasTriggeredByUser) { // Discard events triggered programatically if these types of events haven't been // enabled if (!Parent.IsUsingNonUserEvents && !wasTriggeredByUser) return; if (Enter != null) Enter(sender, EventArgs.Empty); } #endregion #region Class Data protected System.Windows.Forms.RichTextBox textData; // Text data protected int border; // Border of the window protected int spacing; // Spacing between the text and the edge of border protected System.Drawing.Rectangle textRect; // Bounding rectangle for the text protected System.Drawing.Rectangle[] elementRects = new System.Drawing.Rectangle[9]; protected double blinkTime; // Caret blink time in milliseconds protected double lastBlink; // Last timestamp of caret blink protected bool isCaretOn; // Flag to indicate whether caret is currently visible protected int caretPosition; // Caret position, in characters protected bool isInsertMode; // If true, control is in insert mode. Else, overwrite mode. protected int firstVisible; // First visible character in the edit control protected ColorValue textColor; // Text color protected ColorValue selectedTextColor; // Selected Text color protected ColorValue selectedBackColor; // Selected background color protected ColorValue caretColor; // Caret color // Mouse-specific protected bool isMouseDragging; // True to indicate the drag is in progress protected static bool isHidingCaret; // If true, we don't render the caret. #endregion #region Simple overrides/properties/methods /// <summary>Can the edit box have focus</summary> public override bool CanHaveFocus { get { return (IsVisible && IsEnabled); } } /// <summary>Update the spacing</summary> public void SetSpacing(int space) { spacing = space; UpdateRectangles(); } /// <summary>Update the border</summary> public void SetBorderWidth(int b) { border = b; UpdateRectangles(); } /// <summary>Update the text color</summary> public void SetTextColor(ColorValue color) { textColor = color; } /// <summary>Update the text selected color</summary> public void SetSelectedTextColor(ColorValue color) { selectedTextColor = color; } /// <summary>Update the selected background color</summary> public void SetSelectedBackColor(ColorValue color) { selectedBackColor = color; } /// <summary>Update the caret color</summary> public void SetCaretColor(ColorValue color) { caretColor = color; } /// <summary>Get or sets the text</summary> public string Text { get { return textData.Text; } set { SetText(value, false); } } /// <summary>Gets a copy of the text</summary> public string GetCopyText() { return textData.Text.Clone() as string; } #endregion /// <summary>Creates a new edit box control</summary> public EditBox(Dialog parent) : base(parent) { controlType = ControlType.EditBox; parentDialog = parent; border = 5; // Default border spacing = 4; // default spacing isCaretOn = true; textData = new System.Windows.Forms.RichTextBox(); // Create the control textData.Visible = true; textData.Font = new System.Drawing.Font("Arial", 8.0f); textData.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None; textData.Multiline = false; textData.Text = string.Empty; textData.MaxLength = ushort.MaxValue; // 65k characters should be plenty textData.WordWrap = false; // Now create the control textData.CreateControl(); isHidingCaret = false; firstVisible = 0; blinkTime = NativeMethods.GetCaretBlinkTime() * 0.001f; lastBlink = FrameworkTimer.GetAbsoluteTime(); textColor = new ColorValue(0.06f, 0.06f, 0.06f, 1.0f); selectedTextColor = new ColorValue(1.0f, 1.0f, 1.0f, 1.0f); selectedBackColor = new ColorValue(0.15f, 0.196f, 0.36f, 1.0f); caretColor = new ColorValue(0, 0, 0, 1.0f); caretPosition = textData.SelectionStart = 0; isInsertMode = true; isMouseDragging = false; } /// <summary>Set the caret to a character position, and adjust the scrolling if necessary</summary> protected void PlaceCaret(int pos) { // Store caret position caretPosition = pos; // First find the first visible char for (int i = 0; i < textData.Text.Length; i++) { System.Drawing.Point p = textData.GetPositionFromCharIndex(i); if (p.X >= 0) { firstVisible = i; // This is the first visible character break; } } // if the new position is smaller than the first visible char // we'll need to scroll if (firstVisible > caretPosition) firstVisible = caretPosition; } /// <summary>Clears the edit box</summary> public void Clear() { textData.Text = string.Empty; PlaceCaret(0); textData.SelectionStart = 0; } /// <summary>Sets the text for the control</summary> public void SetText(string text, bool selected) { if (text == null) text = string.Empty; textData.Text = text; textData.SelectionStart = text.Length; // Move the care to the end of the text PlaceCaret(text.Length); textData.SelectionStart = (selected) ? 0 : caretPosition; FocusText(); } /// <summary>Deletes the text that is currently selected</summary> protected void DeleteSelectionText() { int first = Math.Min(caretPosition, textData.SelectionStart); int last = Math.Max(caretPosition, textData.SelectionStart); // Update caret and selection PlaceCaret(first); // Remove the characters textData.Text = textData.Text.Remove(first, (last-first)); textData.SelectionStart = caretPosition; FocusText(); } /// <summary>Updates the rectangles used by the control</summary> protected override void UpdateRectangles() { // Get the bounding box first base.UpdateRectangles (); // Update text rect textRect = boundingBox; // First inflate by border to compute render rects textRect.Inflate(-border, -border); // Update the render rectangles elementRects[0] = textRect; elementRects[1] = new System.Drawing.Rectangle(boundingBox.Left, boundingBox.Top, (textRect.Left - boundingBox.Left), (textRect.Top - boundingBox.Top)); elementRects[2] = new System.Drawing.Rectangle(textRect.Left, boundingBox.Top, textRect.Width, (textRect.Top - boundingBox.Top)); elementRects[3] = new System.Drawing.Rectangle(textRect.Right, boundingBox.Top, (boundingBox.Right - textRect.Right), (textRect.Top - boundingBox.Top)); elementRects[4] = new System.Drawing.Rectangle(boundingBox.Left, textRect.Top, (textRect.Left - boundingBox.Left), textRect.Height); elementRects[5] = new System.Drawing.Rectangle(textRect.Right, textRect.Top, (boundingBox.Right - textRect.Right), textRect.Height); elementRects[6] = new System.Drawing.Rectangle(boundingBox.Left, textRect.Bottom, (textRect.Left - boundingBox.Left), (boundingBox.Bottom - textRect.Bottom)); elementRects[7] = new System.Drawing.Rectangle(textRect.Left, textRect.Bottom, textRect.Width, (boundingBox.Bottom - textRect.Bottom)); elementRects[8] = new System.Drawing.Rectangle(textRect.Right, textRect.Bottom, (boundingBox.Right - textRect.Right), (boundingBox.Bottom - textRect.Bottom)); // Inflate further by spacing textRect.Inflate(-spacing, -spacing); // Make the underlying rich text box the same size textData.Size = textRect.Size; } /// <summary>Copy the selected text to the clipboard</summary> protected void CopyToClipboard() { // Copy the selection text to the clipboard if (caretPosition != textData.SelectionStart) { int first = Math.Min(caretPosition, textData.SelectionStart); int last = Math.Max(caretPosition, textData.SelectionStart); // Set the text to the clipboard System.Windows.Forms.Clipboard.SetDataObject(textData.Text.Substring(first, (last-first))); } } /// <summary>Paste the clipboard data to the control</summary> protected void PasteFromClipboard() { // Get the clipboard data System.Windows.Forms.IDataObject clipData = System.Windows.Forms.Clipboard.GetDataObject(); // Does the clipboard have string data? if (clipData.GetDataPresent(System.Windows.Forms.DataFormats.StringFormat)) { // Yes, get that data string clipString = clipData.GetData(System.Windows.Forms.DataFormats.StringFormat) as string; // find any new lines, remove everything after that int index; if ((index = clipString.IndexOf("\n")) > 0) { clipString = clipString.Substring(0, index-1); } // Insert that into the text data textData.Text = textData.Text.Insert(caretPosition, clipString); caretPosition += clipString.Length; textData.SelectionStart = caretPosition; FocusText(); } } /// <summary>Reset's the caret blink time</summary> protected void ResetCaretBlink() { isCaretOn = true; lastBlink = FrameworkTimer.GetAbsoluteTime(); } /// <summary>Update the caret when focus is in</summary> public override void OnFocusIn() { base.OnFocusIn(); ResetCaretBlink(); } /// <summary>Updates focus to the backing rich textbox so it updates it's state</summary> private void FocusText() { // Because of a design issue with the rich text box control that is used as // the backing store for this control, the 'scrolling' mechanism built into // the control will only work if the control has focus. Setting focus to the // control here would work, but would cause a bad 'flicker' of the application. // Therefore, the automatic horizontal scrolling is turned off by default. To // enable it turn this define on. #if (SCROLL_CORRECTLY) NativeMethods.SetFocus(textData.Handle); NativeMethods.SetFocus(Parent.SampleFramework.Window); #endif } /// <summary>Handle keyboard input to the edit box</summary> public override bool HandleKeyboard(Microsoft.Samples.DirectX.UtilityToolkit.NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; // Default to not handling the message bool isHandled = false; if (msg == NativeMethods.WindowMessage.KeyDown) { switch((System.Windows.Forms.Keys)wParam.ToInt32()) { case System.Windows.Forms.Keys.End: case System.Windows.Forms.Keys.Home: // Move the caret if (wParam.ToInt32() == (int)System.Windows.Forms.Keys.End) PlaceCaret(textData.Text.Length); else PlaceCaret(0); if (!NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ShiftKey)) { // Shift is not down. Update selection start along with caret textData.SelectionStart = caretPosition; FocusText(); } ResetCaretBlink(); isHandled = true; break; case System.Windows.Forms.Keys.Insert: if (NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ControlKey)) { // Control insert -> Copy to clipboard CopyToClipboard(); } else if (NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ShiftKey)) { // Shift insert -> Paste from clipboard PasteFromClipboard(); } else { // Toggle insert mode isInsertMode = !isInsertMode; } break; case System.Windows.Forms.Keys.Delete: // Check to see if there is a text selection if (caretPosition != textData.SelectionStart) { DeleteSelectionText(); RaiseChangedEvent(this, true); } else { if (caretPosition < textData.Text.Length) { // Deleting one character textData.Text = textData.Text.Remove(caretPosition, 1); RaiseChangedEvent(this, true); } } ResetCaretBlink(); isHandled = true; break; case System.Windows.Forms.Keys.Left: if (NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ControlKey)) { // Control is down. Move the caret to a new item // instead of a character. } else if (caretPosition > 0) PlaceCaret(caretPosition - 1); // Move one to the left if (!NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ShiftKey)) { // Shift is not down. Update selection // start along with the caret. textData.SelectionStart = caretPosition; FocusText(); } ResetCaretBlink(); isHandled = true; break; case System.Windows.Forms.Keys.Right: if (NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ControlKey)) { // Control is down. Move the caret to a new item // instead of a character. } else if (caretPosition < textData.Text.Length) PlaceCaret(caretPosition + 1); // Move one to the left if (!NativeMethods.IsKeyDown(System.Windows.Forms.Keys.ShiftKey)) { // Shift is not down. Update selection // start along with the caret. textData.SelectionStart = caretPosition; FocusText(); } ResetCaretBlink(); isHandled = true; break; case System.Windows.Forms.Keys.Up: case System.Windows.Forms.Keys.Down: // Trap up and down arrows so that the dialog // does not switch focus to another control. isHandled = true; break; default: // Let the application handle escape isHandled = ((System.Windows.Forms.Keys)wParam.ToInt32()) == System.Windows.Forms.Keys.Escape; break; } } return isHandled; } /// <summary>Handle mouse messages</summary> public override bool HandleMouse(NativeMethods.WindowMessage msg, System.Drawing.Point pt, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; // We need a new point System.Drawing.Point p = pt; p.X -= textRect.Left; p.Y -= textRect.Top; switch(msg) { case NativeMethods.WindowMessage.LeftButtonDown: case NativeMethods.WindowMessage.LeftButtonDoubleClick: // Get focus first if (!hasFocus) Dialog.RequestFocus(this); if (!ContainsPoint(pt)) return false; isMouseDragging = true; NativeMethods.SetCapture(Parent.SampleFramework.Window); // Determine the character corresponding to the coordinates int index = textData.GetCharIndexFromPosition(p); if (index < textData.Text.Length) PlaceCaret(index + 1); else PlaceCaret(index); textData.SelectionStart = caretPosition; FocusText(); ResetCaretBlink(); return true; case NativeMethods.WindowMessage.LeftButtonUp: NativeMethods.ReleaseCapture(); isMouseDragging = false; break; case NativeMethods.WindowMessage.MouseMove: if (isMouseDragging) { // Determine the character corresponding to the coordinates int dragIndex = textData.GetCharIndexFromPosition(p); if (dragIndex < textData.Text.Length) PlaceCaret(dragIndex + 1); else PlaceCaret(dragIndex); } break; } return false; } /// <summary>Handle all other messages</summary> public override bool MsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam) { if (!IsEnabled || !IsVisible) return false; if (msg == NativeMethods.WindowMessage.Character) { int charKey = wParam.ToInt32(); switch(charKey) { case (int)System.Windows.Forms.Keys.Back: { // If there's a selection, treat this // like a delete key. if (caretPosition != textData.SelectionStart) { DeleteSelectionText(); RaiseChangedEvent(this, true); } else if (caretPosition > 0) { // Move the caret and delete the char textData.Text = textData.Text.Remove(caretPosition - 1, 1); PlaceCaret(caretPosition - 1); textData.SelectionStart = caretPosition; FocusText(); RaiseChangedEvent(this, true); } ResetCaretBlink(); break; } case 24: // Ctrl-X Cut case (int)System.Windows.Forms.Keys.Cancel: // Ctrl-C Copy { CopyToClipboard(); // If the key is Ctrl-X, delete the selection too. if (charKey == 24) { DeleteSelectionText(); RaiseChangedEvent(this, true); } break; } // Ctrl-V Paste case 22: { PasteFromClipboard(); RaiseChangedEvent(this, true); break; } case (int)System.Windows.Forms.Keys.Return: // Invoke the event when the user presses Enter. RaiseEnterEvent(this, true); break; // Ctrl-A Select All case 1: { if (textData.SelectionStart == caretPosition) { textData.SelectionStart = 0; PlaceCaret(textData.Text.Length); } break; } // Junk characters we don't want in the string case 26: // Ctrl Z case 2: // Ctrl B case 14: // Ctrl N case 19: // Ctrl S case 4: // Ctrl D case 6: // Ctrl F case 7: // Ctrl G case 10: // Ctrl J case 11: // Ctrl K case 12: // Ctrl L case 17: // Ctrl Q case 23: // Ctrl W case 5: // Ctrl E case 18: // Ctrl R case 20: // Ctrl T case 25: // Ctrl Y case 21: // Ctrl U case 9: // Ctrl I case 15: // Ctrl O case 16: // Ctrl P case 27: // Ctrl [ case 29: // Ctrl ] case 28: // Ctrl \ break; default: { // If there's a selection and the user // starts to type, the selection should // be deleted. if (caretPosition != textData.SelectionStart) { DeleteSelectionText(); } // If we are in overwrite mode and there is already // a char at the caret's position, simply replace it. // Otherwise, we insert the char as normal. if (!isInsertMode && caretPosition < textData.Text.Length) { // This isn't the most efficient way to do this, but it's simple // and shows the correct behavior char[] charData = textData.Text.ToCharArray(); charData[caretPosition] = (char)wParam.ToInt32(); textData.Text = new string(charData); } else { // Insert the char char c = (char)wParam.ToInt32(); textData.Text = textData.Text.Insert(caretPosition, c.ToString()); } // Move the caret and selection position now PlaceCaret(caretPosition + 1); textData.SelectionStart = caretPosition; FocusText(); ResetCaretBlink(); RaiseChangedEvent(this, true); break; } } } return false; } /// <summary>Render the control</summary> public override void Render(Device device, float elapsedTime) { if (!IsVisible) return; // Nothing to render // Render the control graphics for (int i = 0; i <= LowerRightBorder; ++i) { Element e = elementList[i] as Element; e.TextureColor.Blend(ControlState.Normal,elapsedTime); parentDialog.DrawSprite(e, elementRects[i]); } // // Compute the X coordinates of the first visible character. // int xFirst = textData.GetPositionFromCharIndex(firstVisible).X; int xCaret = textData.GetPositionFromCharIndex(caretPosition).X; int xSel; if (caretPosition != textData.SelectionStart) xSel = textData.GetPositionFromCharIndex(textData.SelectionStart).X; else xSel = xCaret; // Render the selection rectangle System.Drawing.Rectangle selRect = System.Drawing.Rectangle.Empty; if (caretPosition != textData.SelectionStart) { int selLeft = xCaret, selRight = xSel; // Swap if left is beigger than right if (selLeft > selRight) { int temp = selLeft; selLeft = selRight; selRight = temp; } selRect = System.Drawing.Rectangle.FromLTRB( selLeft, textRect.Top, selRight, textRect.Bottom); selRect.Offset(textRect.Left - xFirst, 0); selRect.Intersect(textRect); Parent.DrawRectangle(selRect, selectedBackColor); } // Render the text Element textElement = elementList[TextLayer] as Element; textElement.FontColor.Current = textColor; parentDialog.DrawText(textData.Text.Substring(firstVisible), textElement, textRect); // Render the selected text if (caretPosition != textData.SelectionStart) { int firstToRender = Math.Max(firstVisible, Math.Min(textData.SelectionStart, caretPosition)); int numToRender = Math.Max(textData.SelectionStart, caretPosition) - firstToRender; textElement.FontColor.Current = selectedTextColor; parentDialog.DrawText(textData.Text.Substring(firstToRender, numToRender), textElement, selRect); } // // Blink the caret // if(FrameworkTimer.GetAbsoluteTime() - lastBlink >= blinkTime) { isCaretOn = !isCaretOn; lastBlink = FrameworkTimer.GetAbsoluteTime(); } // // Render the caret if this control has the focus // if( hasFocus && isCaretOn && !isHidingCaret ) { // Start the rectangle with insert mode caret System.Drawing.Rectangle caretRect = textRect; caretRect.Width = 2; caretRect.Location = new System.Drawing.Point( caretRect.Left - xFirst + xCaret -1, caretRect.Top); // If we are in overwrite mode, adjust the caret rectangle // to fill the entire character. if (!isInsertMode) { // Obtain the X coord of the current character caretRect.Width = 4; } parentDialog.DrawRectangle(caretRect, caretColor); } } } }